DateTime column type becomes String type after deserializing DataTable property on Custom Class

前端 未结 1 1567
一向
一向 2020-12-22 11:18

My question is very similar to this one however I don\'t have enough reputation to post a comment on the original answer.

I\'ve got a custom class called FillPDF tha

相关标签:
1条回答
  • 2020-12-22 11:42

    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));
            }
        }
    }
    

    Then add DataSetConverter<TypeInferringDataTableConverter> to your list of converters.

    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.
      //
      // ...
      
    • Have your forked converter subclass Newtonsoft's DataTableConverter; remove all code for WriteJson().

    • 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));
          }
      }
      
    • Then fix the call to GetColumnDataType() to pass in the column name around line 152:

      Type columnType = GetColumnDataType(reader, columnName);
      
    • Stub in any missing internal methods such as ReadAndAssert() with static extensions methods as shown here.

    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?.

    0 讨论(0)
提交回复
热议问题