在自定义类上反序列化DataTable属性后,DateTime列类型变为String类型 [英] DateTime column type becomes String type after deserializing DataTable property on Custom Class
问题描述
我的问题与这非常相似一个,但是我没有足够的声誉在原始答案上发表评论.
My question is very similar to this one however I don't have enough reputation to post a comment on the original answer.
我有一个名为FillPDF的自定义类,该类正在服务器上进行序列化,并在客户端上进行反序列化.
I've got a custom class called FillPDF that I'm serializing on the server and deserializing on the client.
FillPDF dsPDF = JsonConvert.DeserializeObject<FillPDF>(json);
FillPDF
类由DataSet
属性组成,该属性包含DataTables
The FillPDF
class consists of a DataSet
property which contains a collection of DataTables
通过阅读原始问题的解决方案,我知道了为什么DateTime
类型被不正确地设置为String
的原因.我了解Json.Net的DataTableConverter
通过仅查看第一行来推断每个DataColumn.DataType
(我的第一行具有NULL
值).
From reading the solution to the original question I'm aware of why the DateTime
type is improperly being set as a String
. I understand Json.Net's DataTableConverter
infers each DataColumn.DataType
by looking at the first row only (my first row has NULL
values).
我尝试从原始问题开始实施解决方案. dbc建议覆盖DataTableConverter
.我已经这样做了,并且在序列化和反序列化过程中使用了settings
对象,如下所示:
I tried to implement the solution from the original question. Dbc had suggested overriding the DataTableConverter
. I've done it as such and I'm using the settings
object during Serialization and Deserialization like so:
// 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
.
However I'm not getting any errors and my underlying DataTables are still not being correctly Deserialized. I assume this is because I'm serializing/deserializing a custom object as opposed to simply a DataTable
like in the original question.
我想做的是:
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
.
Presumably this would have to be added to the overridden TypeInferringDataTableConverter
.
我不太确定从这里要去哪里,所以我急切需要一些帮助,转向SO!
I'm not too sure where to go from here, so I'm turning to SO in dire need of some help!
谢谢
贾斯汀.
推荐答案
问题似乎是Newtonsoft的DataTableConverter converter = new DataTableConverter();然后直接调用其ReadJson()
方法,Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/DataTableConverter.cs"rel =" nofollow noreferrer> DataTableConverter
.因此,您的转换器永远不会被使用.
The problem seems to be that Newtonsoft's DataSetConverter
hardcodes using Newtonsoft's DataTableConverter
by doing DataTableConverter converter = new DataTableConverter();
and then calling its ReadJson()
method directly. Thus your converter is never used.
One solution would be to create your own version of DataSetConverter
as well by adapting James Newton-King's original code:
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>
添加到您的转换器列表中.
Then add DataSetConverter<TypeInferringDataTableConverter>
to your list of converters.
顺便说一句,如果当列名包含字符串"date"
时,您要做的只是将列类型设置为DateTime
,那么您可以考虑使用以下方法在转换器的各行中创建一个比TypeInferringDataTableConverter
更简单的转换器: 反序列化缺少第一列的数据表:
Incidentally, if all you need to do is to set the column type to DateTime
when the column name includes the string "date"
, you might consider creating a simpler converter than TypeInferringDataTableConverter
along the lines of the converter from deserialize a datatable with a missing first column:
Fork the code of
DataTableConverter
. Take note of the license at the beginning:
// 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.
//
// ...
具有分叉的转换器子类Newtonsoft的DataTableConverter
;删除WriteJson()
的所有代码.
Have your forked converter subclass Newtonsoft's DataTableConverter
; remove all code for WriteJson()
.
修改GetColumnDataType()
以传递列名并添加必要的逻辑:
Modify GetColumnDataType()
to pass in the column name and add the necessary logic:
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));
}
}
然后将调用修复为 GetColumnDataType()
在第152行附近输入列名称:
Then fix the call to GetColumnDataType()
to pass in the column name around line 152:
Type columnType = GetColumnDataType(reader, columnName);
在任何缺少的内部方法(例如ReadAndAssert()
)中添加存根,并使用静态扩展方法,如此处所示.
Stub in any missing internal methods such as ReadAndAssert()
with static extensions methods as shown here.
用于创建自己的Newtonsoft转换器版本的替代解决方案是在如何更改数据表中数据列的数据类型?中的一个答案,将"date"
列为DateTime
列..
An alternate solution to creating your own versions of Newtonsoft's converters would be, in an [OnDeserialized]
event in the container class, to loop through all columns in all tables in the DataSet
and convert columns of type string
(or object
) whose name contains "date"
to DateTime
columns, using one of the answers from How To Change DataType of a DataColumn in a DataTable?.
这篇关于在自定义类上反序列化DataTable属性后,DateTime列类型变为String类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!