相關問題" type="hidden"/>

Newtonsoft.Json.Linq.JObject.GetValue("[key]").ToObject 相關問題


 

我們用Newtonsoft.Json.Linq.JObject.GetValue("[key]").ToObject<DataTable>方法把一個JObject對象中的某個節點轉換成DataTable格式的數據很方便。一直這樣用也沒出什么問題。但是最近發現在某些情況下,這種數據轉換其實是有一定的問題甚至報錯。場景如下:

1. 當數據源類似如下時。當把Data節點的數據轉換成DataTable格式時,會默認abc字段為整型,導致在對第二條數據做轉換時,50.11會丟失精度,變成整型50

"{\"Data\":[{\"abc\":50},{\"abc\":50.01}]}"

2. 當數據源類似如下時。就會導致第二條數據在轉化成INT類型時出錯。

"{\"Data\":[{\"abc\":50},{\"abc\":\"xyz\"}]}"

 

這個就需要用到ToObject的JsonSerializer參數了。通過重寫serializer方法,來具體指定某一列的數據類型,從而達到效果。

public class CustomDataTableConverter : DataTableConverter
{
    private static void CreateRow(JsonReader reader, DataTable dt)
    {
        DataRow row = dt.NewRow();
        reader.Read();

        while(reader.TokenType == JsonToken.PropertyName)
        {
            string columnName = (string)reader.Value;
            reader.Read();
            DataColumn column = dt.Columns[columnName];
            if(column == null)
            {
                Type columnDataType = GetColumnDataType(reader);
                column = new DataColumn(columnName, columnDataType);
                dt.Columns.Add(column);
            }
            if(column.DataType == typeof(DataTable))
            {
                 if(reader.TokenType == JsonToken.StartArray)
                 {
                     reader.Read();
                 }
                 DataTable table = new DataTable();
                 while(reader.TokenType != JsonToken.EndArray)
                 {
                     CreateRow(reader, table);
                     reader.Read();
                 }
                 row[columnName] = table;
            }
            else if(column.DataType.IsArray && (column.DataType != typeof(byte[])))
            {
                if(reader.TokenType == JsonToken.StartArray)
                 {
                     reader.Read();
                 }
                 List<object> list = new List<object>();
                 while(reader.TokenType != JsonToken.EndArray)
                 {
                     list.Add(reader.Value);
                     reader.Read();
                 }
                 Array destinationArray = Array.CreateInstance(column.DataType.GetElementType(), list.Count);
                 Array.Copy(list.ToArray(), destinationArray, list.Count);
                 row[columnName] = destinationArray;
            }
            else
            {
                object val = DBNull.Value;
                if(reader.Value != null)
                {
                    if(column.DataType == typeof(string))
                    {
                         val = reader.Value.ToString();
                    }
                    else
                    {
                        if(string.IsNullOrEmpty(reader.Value.ToString()))
                        {
                            val = DBNull.Value;
                        }
                        else
                        {
                            val = Convert.ChangeType(reader.Value, column.DataType);
                        }
                    }
                    row[columnName] = val;
                }
                reader.Read();
            }    
            row.EndEdit();
            dt.Rows.Add(row);
        }
    }  

    private static Type GetColumnDataType(JsonReader reader)
    {
        JsonToken tokenType = reader.TokenType;
        switch(tokenType)
        {
            case JsonToken.StartArray:
                reader.Read();
                if(reader.TokenType != JsonToken.StartObject)
                {
                     return GetColumnDataType(reader).MakeArrayType();
                }
                return typeof(DataTable);
            //這個地方需要對數值類型做特殊處理。如果能確認是數值列,那么可以用double類型替代,防止精度丟失。如果是字符串類型列,那么使用string類型,防止數據轉化出錯     
            case JsonToken.Integer:
                return typeof(double);
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Date:
            case JsonToken.Bytes:
                return reader.ValueType;
            case JsonToken.Null:
            case JsonToken.Undefined:
                return typeof(string);
        }
        throw new JsonException($"Unexpected JSON token while reading DataTable: {tokenType}");
    }

    private static void MakeTableSchema(string schema, DataTable dt)
    {
        if(!string.IsNullOrEmpty(schema) && dt != null)
        {
            string[] colinfos = schema.Trim(',').Split(',');
            foreach(string colinfo in colinfos)
            {
                string[] info = colinfo.split('|');
                if(!dt.Columns.Contains(info[0]))
                {
                    dt.Columns.Add(info[0], Type.GetType("System." + info[i]));
                } 
            }
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        DataTable dt = existingValue as DataTable;
        if(dt == null)
        {
            dt = (objectType == typeof(DataTable)) ? new DataTable() : ((DataTable)Activator.CreateInstance(objectType));
        }
        if(reader.TokenType == JsonToken.PropertyName)
        {
            dt.TableName = (string)reader.Value;
            reader.Read();
        }
        if(reader.TokenType == JsonToken.StartObject)
        {
            reader.Read();
            while(reader.TokenType == JsonToken.PropertyName)
            {
                if((string)reader.Value == "SCHEMA")
                {
                    reader.Read();
                    MakeTableSchema((string)reader.Value, dt);
                    reader.Read();
                }
                if((string)reader.Value == "TABLENAME")
                {
                    reader.Read();
                    dt.TableName = (string)reader.Value;
                    reader.Read();
                }
                else if((string)reader.Value == "DATA")
                {
                    reader.Read();
                    if(reader.TokenType == JsonToken.StartArray)
                    {
                        reader.Read();
                    }
                    while(reader.TokenType != JsonToken.EndArray)
                    {
                         CreateRow(reader, dt);
                         reader.Read();
                     }
                }
            }
            reader.Read();
        }
        else if(reader.TokenType == JsonToken.StartArray)
        {
            reader.Read();
            while(reader.TokenType != JsonToken.EndArray)
            {
                Create(reader, dt);
                reader.Read();
            }
        }
        return dt;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        DataTable table = (DataTable)value;
        DefaultContractResolver contractResolver = serializer.ContractResolver as DefaultContractResolver;
        StringBuilder sb = new StringBuilder();
        foreach(DataColumns column in table.Columns)
        {
            sb.Append(column.ColumnName + "|" + column.DataType.Name + ",");
        }
        writer.WriteStartObject();
        writer.WritePropertyName("SCHEMA");
        serializer.Serializer(writer, sb.ToString());
        writer.WritePropertyName("TABLENAME");
        serializer.Serialize(writer, table.TableName);
        writer.WritePropertyName("DATA");
        writer.WriteStartArray();
        foreach(DataRow row in table.Rows)
        {
            writer.WriteStartObject();
            foreach(DataColumn column in row.Table.Columns)
            {
                 if((serializer.NullValueHandling != NullValueHanding.Ignore) || ((row[column] != null) && (row[column] != DBNull.Value)))
                 {
                     writer.WritePropertyName((contractResolver != null) ? contractResolver.GetResolvedPropertyName(column.ColumnName) : column.ColumnName);
                     serializer.Serialize(writer, row[column]);
                 }
            }
            writer.WriteEndObject();
        }
        writer.WriteEndArray();
        writer.WriteEndObject();
    }
}                                        

 

調用方法

JsonSerializer serializer = new JsonSerializer();
serializer.Converts.Add(new CustomDataTableConverter());
JObject.GetValue("[Key]").ToObject<DataTable>(serializer);

 

這樣就能相對正確地把數據轉成DataTable。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM