【问题标题】:DateTime column type becomes String type after deserializing DataTable property on Custom Class反序列化自定义类上的 DataTable 属性后,日期时间列类型变为字符串类型
【发布时间】:2018-03-03 04:34:02
【问题描述】:

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

我想做的是:

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 急需一些帮助!

谢谢,

贾斯汀。

【问题讨论】:

  • 问题似乎是 DataSetConverter 通过执行 DataTableConverter converter = new DataTableConverter(); 使用 Newtonsoft 的 DataTableConverter 硬编码。您可能还需要创建自己的 DataSetConverter 版本。
  • 感谢您的及时回复!我会考虑覆盖DataSetConverter

标签: c# json serialization datatable


【解决方案1】:

问题似乎是 Newtonsoft 的 DataSetConverter 硬编码使用 Newtonsoft 的 DataTableConverter 通过执行 DataTableConverter converter = new DataTableConverter(); 然后直接调用其 ReadJson() 方法。因此,您的转换器永远不会被使用。

一种解决方案是通过改编 James Newton-King 的 original code 来创建您自己的 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&lt;TypeInferringDataTableConverter&gt; 添加到您的转换器列表中。

顺便说一句,如果您需要做的只是在列名包含字符串 "date" 时将列类型设置为 DateTime,那么您可以考虑创建一个比 TypeInferringDataTableConverter 更简单的转换器deserialize a datatable with a missing first column:

  • fork DataTableConverter 的代码。记下开头的许可证:

    // 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() 的所有代码。

  • 修改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));
        }
    }
    
  • 然后修复对GetColumnDataType() 的调用以传入第 152 行附近的列名:

    Type columnType = GetColumnDataType(reader, columnName);
    
  • 在任何缺少的内部方法中存根,例如 ReadAndAssert() 和静态扩展方法,如 here 所示。

另一种解决方案创建您自己的 Newtonsoft 转换器版本将是,在容器类中的 [OnDeserialized] 事件中,循环遍历 DataSet 中所有表中的所有列并转换string(或object)类型的列,其名称包含"date"DateTime 列,使用来自How To Change DataType of a DataColumn in a DataTable? 的答案之一。

【讨论】:

  • 感谢您的帮助!我已经实现了我自己的使用 TypeInferringDataTableConverter 的自定义 DataSetConverter,似乎运行良好。再次感谢您昨天的及时回复以及您提供的所有帮助!
猜你喜欢
  • 2016-09-03
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多