且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在数字反序列化中更改默认类型

更新时间:2022-05-23 22:19:54

代码不起作用的原因是,在最初推断列类型时,DataTableConverter不会尝试对遇到的列的第一个值进行反序列化。相反,它只使用JsonReader.Read()读取,然后在DataTableConverter.GetColumnDataType()中将列类型设置为等于观察到的标记类型。您的方法PrimitiveJsonConverter.Read()此时未被调用。而且,由于JsonReader.Read()旨在为整数值返回long而不是int,因此数据表列类型以long结束。

您有几个选项可以覆盖Newtonsoft的默认行为并获取Int32列类型:

  1. 您可以使用typed DataSet。在这种情况下,将预定义列类型。

  2. 您可以使用PreferInt32JsonTextReaderthis answerOverriding Default Primitive Type Handling in Json.Net(Json.NET 10.0.1或更高版本)阅读。

  3. 您可以在反序列化后将列转换为Int32。首先介绍一下扩展方法:

    public static class DataTableExtensions
    {
        public static DataTable RemapInt64ColumnsToInt32(this DataTable table)
        {
            if (table == null)
                throw new ArgumentNullException();
            for (int iCol = 0; iCol < table.Columns.Count; iCol++)
            {
                var col = table.Columns[iCol];
                if (col.DataType == typeof(Int64)
                    && table.AsEnumerable().Where(r => !r.IsNull(col)).Select(r => (Int64)r[col]).All(i => i >= int.MinValue && i <= int.MaxValue))
                {
                    ReplaceColumn(table, col, typeof(Int32), (o, t) => o == null ? null : Convert.ChangeType(o, t, NumberFormatInfo.InvariantInfo));
                }
            }
            return table;
        }
    
        private static DataColumn ReplaceColumn(DataTable table, DataColumn column, Type newColumnType, Func<object, Type, object> map)
        {
            var newValues = table.AsEnumerable()
                .Select(r => r.IsNull(column) ? (object)DBNull.Value : map(r[column], newColumnType))
                .ToList();
    
            var ordinal = column.Ordinal;
            var name = column.ColumnName;
            var @namespace = column.Namespace;
    
            var newColumn = new DataColumn(name, newColumnType);
            newColumn.Namespace = @namespace;
            table.Columns.Remove(column);
            table.Columns.Add(newColumn);
            newColumn.SetOrdinal(ordinal);
    
            for (int i = 0; i < table.Rows.Count; i++)
                if (!(newValues[i] is DBNull))
                    table.Rows[i][newColumn] = newValues[i];
    
            return newColumn;
        }    
    }
    

    然后执行:

    var myDataSet = JsonConvert.DeserializeObject<DataSet>(json);
    myDataSet.Tables.Cast<DataTable>().Aggregate((object)null, (o, dt) => dt.RemapInt64ColumnsToInt32());
    

    相关:How To Change DataType of a DataColumn in a DataTable?

  4. 您可以派生您自己的DataTableConverter版本,然后修改DataTableConverter.GetColumnDataType()的逻辑,为JsonToken.Integer令牌返回typeof(Int32)

    有关所涉及内容的示例,请参阅this answerdeserialize a datatable with a missing first column

    由于您的根对象是DataSet,因此您还需要派生您自己的DataSetConverter版本,并使其使用自定义的DataTableConverter,如this answerDateTime column type becomes String type after deserializing DataTable property on Custom Class中所示。

    /li>

opasks其性能如何.?

您必须对其进行测试并查看,请参见https://ericlippert.com/2012/12/17/performance-rant/

也就是说,一般来说,对于庞大的数据集,您希望避免在最终反序列化之前以某种中间表示形式(例如,JToken层次结构或单个大型string)将整个数据集加载到内存中。选项#1、#2和#4避免这样做。#3确实将部分数据加载到中间表示中;一些(但不是所有)DataTable列最终被加载,然后被替换。因此,性能可能是正常的,但也可能不是--您需要检查。