2015-09-17 3 views
1

Я хочу сериализовать и десериализовать Nullable DateTime to/from JSON, но я не хочу комментировать его JsonConverterAttribute. Тем не менее, я хотел бы сохранить его сразу в JsonSerializerSettings, не раздувая DTO с этими атрибутами, сохраняя DTOs как обычно.Дата не может быть сериализована или десериализована

Вот DTO:

public class Post 
{ 
    public DateTime? Created { get; set; } 
} 

Вот Custom JsonConverter:

internal class EpochDateTimeConverter : Newtonsoft.Json.JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(DateTime).IsAssignableFrom(objectType); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var t = (long)Convert.ToDouble(reader.Value.ToString()); 
     return t.FromUnixTime(); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     long ticks; 
     DateTime valueAsDate = (DateTime)value; 
     if (valueAsDate != DateTime.MinValue) 
     { 
      if (value is DateTime) 
      { 
       var epoc = new DateTime(1970, 1, 1); 
       var delta = (valueAsDate) - epoc; 
       if (delta.TotalSeconds < 0) 
       { 
        throw new ArgumentOutOfRangeException("Unix epoc starts January 1st, 1970"); 
       } 
       ticks = (long)delta.TotalSeconds; 
      } 
      else 
      { 
       throw new Exception("Expected date object value."); 
      } 
      writer.WriteValue(ticks); 
     } 
    } 
} 

Вот минимальный репродукция:

using Newtonsoft.Json; 
using System; 
using System.Collections.Generic; 
namespace NameSpaceSample 
{ 
    public class Post 
    { 
    public DateTime? Created { get; set; } 
    } 

    public class Program 
    { 
     public static void Main(string[] args) 
     { 
     var settings = new JsonSerializerSettings() 
     { 
      NullValueHandling = NullValueHandling.Ignore, 
      Converters = new List<JsonConverter> 
      { 
       new EpochDateTimeConverter() 
      } 
     }; 

     string postAsJson = JsonConvert.SerializeObject(new Post { Created = DateTime.UtcNow }, settings); 

     Console.WriteLine(postAsJson);// {"Created":"2015-09-17T17:15:06.6160689Z"} 

     var json = "{\"Created\":1442510191}"; 

     Post post = JsonConvert.DeserializeObject<Post>(json, settings);//Exception here 

     Console.ReadKey(); 
     } 
    } 
} 

Исключения брошено в этой линии:

JsonReaderException: 
Error reading date. Unexpected token: Integer. Path 'Created', line 1, position 21. 

Примечание:

Я знаю, что это может быть решено только аннотирования его с JsonConverterAttribute, как показано ниже, но я не хочу, чтобы сделать это для вышеупомянутого причина.

public class Post 
{ 
    [JsonConverter(typeof(EpochDateTimeConverter))] 
    public DateTime? Created { get; set; } 
} 
+0

Обратите внимание, что эпоха Unix в терминах UTC, но данный 'DateTime' может быть с точки зрения местных, МСВ, или какой-либо другой неопределенный часовой пояс (см. 'DateTimeKind'). Если вы не уверены, что все ваши значения «DateTime» соответствуют UTC, я бы не использовал этот подход. Лучшей практикой для дат в JSON является использование ISO8601, который уже предоставлен JSON.net. –

+0

@MattJohnson Мне это известно. Спасибо, в любом случае :) –

ответ

0

Понял это самостоятельно. Я просто должен был изменить CanConvert функцию следующим образом:

public override bool CanConvert(Type objectType) 
{ 
    return objectType == typeof(DateTime) || objectType == typeof(DateTime?); 
} 

Это не очень легко узнать. Полагая мой ответ здесь, чтобы помочь другим, если они когда-нибудь столкнутся с этим.

0

Даю вам путь от любого объекта к другому объекту, так что вы не переживайте передавать что-либо, благодаря

public static T ConvertTo<T>(this object value) 
     { 
      T returnValue = default(T); 

      if (value is T) 
      { 
       returnValue = (T)value; 
      } 
      else 
      { 
       try 
       { 
        returnValue = (T)Convert.ChangeType(value, typeof(T)); 
       } 
       catch (InvalidCastException) 
       { 
        returnValue = default(T); 
       } 
      } 

      return returnValue; 
     } 
0

Способ по JsonConverterCanConvert(Type objectType) определяет, что преобразователь будет использоваться для текущего свойства который сериализуется/десериализуется.

Как тип вашего имущества DateTime?, и это не может быть назначено с DateTime, оно возвращает false, а конвертер тогда не используется.

Вам просто нужно изменить метод к следующему:

public override bool CanConvert(Type objectType) 
{ 
    return typeof(DateTime?).IsAssignableFrom(objectType); 
} 
Смежные вопросы