在自定义类上反序列化DataTable属性后,DateTime列类型变为String类型

时间:2017-09-21 18:00:50

标签: c# json serialization datatable

我的问题与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!

谢谢,

贾斯汀。

1 个答案:

答案 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

创建自己版本的Newtonsoft转换器的

备用解决方案将在容器类的GetColumnDataType()事件中循环遍历{{1}中所有表中的所有列使用here中的一个答案,将名称中包含string的{​​{1}}(或object)列转换为"date"列。