且构网

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

更改数字反序列化的默认类型

更新时间:2023-02-20 18:06:26

您的代码无法正常工作的原因是,在最初推断列类型时,是 JsonReader.Read() ,然后在

The reason your code does not work is that, when initially inferring column type, DataTableConverter does not attempt to deserialize the first value encountered for a column. Instead, it simply reads it using JsonReader.Read() and then sets the column type equal to the observed token type, in DataTableConverter.GetColumnDataType(). Your method PrimitiveJsonConverter.Read() just isn't getting called at this point. And, since JsonReader.Read() is designed to return a long instead of an int for integer values, the data table column types end up as long.

您可以选择一些选项来覆盖Newtonsoft的默认行为并获取Int32列类型:

You have a few options to override Newtonsoft's default behavior and get Int32 column types:

  1. 您可以使用

  1. You could use a typed DataSet. In this situation the column types will be predefined.

您可以使用PreferInt32JsonTextReader此答案中读取

You could read using PreferInt32JsonTextReader from this answer to Overriding Default Primitive Type Handling in Json.Net (Json.NET 10.0.1 or later).

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

You could convert columns to Int32 after deserialization. First, introduce the following extension method:

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());

相关: 如何更改数据表中数据列的数据类型? .

您可以派生自己的 DataTableConverter 并修改

You could fork your own version of DataTableConverter and modify the logic of DataTableConverter.GetColumnDataType() to return typeof(Int32) for JsonToken.Integer tokens.

有关所涉及内容的示例,请参见 ="a href =" https:"此答案://***.com/q/32726718/3744182>反序列化缺少第一列的数据表 .

For an example of what would be involved, see this answer to deserialize a datatable with a missing first column.

由于您的根对象是DataSet,因此您还需要派生自己的 在自定义类中对DataTable属性进行反序列化之后,DateTime列类型变为String类型 a> .

Since your root object is a DataSet, you would also need to fork your own version of DataSetConverter and make it use your customized DataTableConverter, as shown in this answer to DateTime column type becomes String type after deserializing DataTable property on Custom Class.

OP 请求其性能如何...?

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

You have to test it and see, see https://ericlippert.com/2012/12/17/performance-rant/.

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

That being said, in general, with huge data sets, you want to avoid loading the entire data set into memory in some intermediate representation (e.g. a JToken hierarchy or single large string) before final deserialization. Options #1, #2 and #4 avoid doing so. #3 does load a portion of the data into an intermediate representation; some but not all DataTable columns end up being loaded and then replaced. Thus performance may be OK, but maybe not -- you need to check.