我的问题与this one非常相似,但我没有足够的声誉对原始答案发表评论。
我有一个名为FillPDF的自定义类,我在服务器上进行序列化并在客户端进行反序列化。
FillPDF dsPDF = JsonConvert.DeserializeObject<FillPDF>(json);
FillPDF
类包含DataSet
属性,其中包含DataTables
从阅读解决方案到原始问题,我了解为什么DateTime
类型被错误地设置为String
。我了解Json.Net的DataTableConverter
只是通过查看第一行(我的第一行有DataColumn.DataType
个值)来推断每个NULL
。
我尝试从原始问题实施解决方案。 Dbc曾建议覆盖DataTableConverter
。我已经这样做了,我在序列化和反序列化过程中使用了settings
对象,如下所示:
// Server
FillPDF pdfData = new FillPDF(strUniqueColID);
var settings = new JsonSerializerSettings { Converters = new[] { new TypeInferringDataTableConverter() } };
string json = JsonConvert.SerializeObject(pdfData, Formatting.Indented,settings);
// Client
var settings = new JsonSerializerSettings { Converters = new[] { new TypeInferringDataTableConverter() } };
FillPDF dsPDF = JsonConvert.DeserializeObject<FillPDF>(json,settings);
但是我没有收到任何错误,我的基础DataTable仍未正确反序列化。我认为这是因为我在序列化/反序列化自定义对象,而不是原始问题中的DataTable
。
我希望能做的是:
if(c.ColumnName.toLower().Contains("date"))
{
// Set Column's Type to DateTime because I know all Column Names containing "date" should be of type DateTime
}
据推测,这必须添加到被覆盖的TypeInferringDataTableConverter
。
我不太确定从哪里开始,所以我需要一些帮助才能转向SO!
谢谢,
贾斯汀。
答案 0 :(得分:1)
问题似乎是Newtonsoft的link硬编码,使用Newtonsoft的change log执行ReadJson()
,然后直接调用其DataSetConverter
方法。因此,您的转换器永远不会被使用。
一个解决方案就是通过改编James Newton-King的DataSetConverter
来创建自己的public class DataSetConverter<TDataTableConverter> : DataSetConverter where TDataTableConverter : JsonConverter, new()
{
// This code adapted from
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/DataSetConverter.cs
// Copyright (c) 2007 James Newton-King
// Licensed under The MIT License (MIT):
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md
readonly TDataTableConverter converter = new TDataTableConverter();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
// handle typed datasets
DataSet ds = (objectType == typeof(DataSet))
? new DataSet()
: (DataSet)Activator.CreateInstance(objectType);
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.PropertyName)
{
DataTable dt = ds.Tables[(string)reader.Value];
bool exists = (dt != null);
dt = (DataTable)converter.ReadJson(reader, typeof(DataTable), dt, serializer);
if (!exists)
{
ds.Tables.Add(dt);
}
reader.ReadAndAssert();
}
return ds;
}
}
public static class JsonReaderExtensions
{
public static void ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
{
new JsonReaderException(string.Format("Unexpected end at path {0}", reader.Path));
}
}
}
版本:
DataSetConverter<TypeInferringDataTableConverter>
然后将DateTime
添加到转换器列表中。
顺便提一下,如果您需要做的只是在列名称包含字符串"date"
时将列类型设置为TypeInferringDataTableConverter
,您可以考虑创建一个比// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// ...
更简单的转换器来自DataTableConverter
的转换器行:
分叉original code的代码。请注意开头的许可证:
DataTableConverter
让您的分叉转换器子类为Newtonsoft的WriteJson()
;删除GetColumnDataType()
的所有代码。
修改private static Type GetColumnDataType(JsonReader reader, string columnName)
{
JsonToken tokenType = reader.TokenType;
switch (tokenType)
{
case JsonToken.String:
if (columnName.IndexOf("date", StringComparison.OrdinalIgnoreCase) >= 0)
return typeof(DateTime);
return reader.ValueType;
case JsonToken.Integer:
case JsonToken.Boolean:
case JsonToken.Float:
case JsonToken.Date:
case JsonToken.Bytes:
return reader.ValueType;
case JsonToken.Null:
case JsonToken.Undefined:
if (columnName.IndexOf("date", StringComparison.OrdinalIgnoreCase) >= 0)
return typeof(DateTime);
return typeof(string);
case JsonToken.StartArray:
reader.ReadAndAssert();
if (reader.TokenType == JsonToken.StartObject)
{
return typeof(DataTable); // nested datatable
}
Type arrayType = GetColumnDataType(reader, columnName);
return arrayType.MakeArrayType();
default:
throw JsonSerializationException.Create(reader, "Unexpected JSON token when reading DataTable: {0}".FormatWith(CultureInfo.InvariantCulture, tokenType));
}
}
以传入列名并添加必要的逻辑:
Type columnType = GetColumnDataType(reader, columnName);
然后将调用修复为deserialize a datatable with a missing first column以传入第152行周围的列名:
ReadAndAssert()
使用DataTableConverter
所示的静态扩展名方法存储任何缺少的内部方法,例如DataSet
。
备用解决方案将在容器类的GetColumnDataType()
事件中循环遍历{{1}中所有表中的所有列使用here中的一个答案,将名称中包含string
的{{1}}(或object
)列转换为"date"
列。