更新时间:2022-05-23 22:19:54
代码不起作用的原因是,在最初推断列类型时,DataTableConverter
不会尝试对遇到的列的第一个值进行反序列化。相反,它只使用JsonReader.Read()
读取,然后在DataTableConverter.GetColumnDataType()
中将列类型设置为等于观察到的标记类型。您的方法PrimitiveJsonConverter.Read()
此时未被调用。而且,由于JsonReader.Read()
旨在为整数值返回long
而不是int
,因此数据表列类型以long
结束。
您有几个选项可以覆盖Newtonsoft的默认行为并获取Int32
列类型:
您可以使用typed DataSet
。在这种情况下,将预定义列类型。
您可以使用PreferInt32JsonTextReader
从this answer到Overriding Default Primitive Type Handling in Json.Net(Json.NET 10.0.1或更高版本)阅读。
您可以在反序列化后将列转换为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());
您可以派生您自己的DataTableConverter
版本,然后修改DataTableConverter.GetColumnDataType()
的逻辑,为JsonToken.Integer
令牌返回typeof(Int32)
。
有关所涉及内容的示例,请参阅this answer至deserialize a datatable with a missing first column。
由于您的根对象是DataSet
,因此您还需要派生您自己的DataSetConverter
版本,并使其使用自定义的DataTableConverter
,如this answer到DateTime column type becomes String type after deserializing DataTable property on Custom Class中所示。
opasks,其性能如何.?
您必须对其进行测试并查看,请参见https://ericlippert.com/2012/12/17/performance-rant/。
也就是说,一般来说,对于庞大的数据集,您希望避免在最终反序列化之前以某种中间表示形式(例如,JToken
层次结构或单个大型string
)将整个数据集加载到内存中。选项#1、#2和#4避免这样做。#3确实将部分数据加载到中间表示中;一些(但不是所有)DataTable
列最终被加载,然后被替换。因此,性能可能是正常的,但也可能不是--您需要检查。