2016-04-29 79 views
1

我想使用JSON.Net序列化为BSON,但原始偏移似乎不被尊重。JSON.Net BSON序列化不正确地处理DateTimeOffset?

你能看到一个问题,我试图让这项工作?

[Test] 
public void SerializeDateTimeOffsetToBson() 
{ 
    var serializer = new Newtonsoft.Json.JsonSerializer { 
     TypeNameHandling = TypeNameHandling.Auto, 
     DateParseHandling = DateParseHandling.DateTimeOffset, 
     DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind 
    }; 

    var negOffset = new DateTimeOffset(2014, 7, 10, 0, 0, 0, new TimeSpan(-5, 0, 0)); 
    var gmtOffset = new DateTimeOffset(2014, 7, 10, 0, 0, 0, new TimeSpan()); 
    var posOffset = new DateTimeOffset(2014, 7, 10, 0, 0, 0, new TimeSpan(5, 0, 0)); 

    var dt = new { 
     negOffset = negOffset, 
     gmtOffset = gmtOffset, 
     posOffset = posOffset 
    }; 

    byte[] serialized; 

    using (var ms = new MemoryStream()) 
    using (var writer = new BsonWriter(ms)) { 
     serializer.Serialize(writer, dt); 
     writer.Close(); 
     serialized = ms.ToArray(); 
    } 

    dynamic deserializedDt; 

    using (var ms = new MemoryStream(serialized)) 
    using (var rdr = new BsonReader(ms)) { 
     deserializedDt = (dynamic)serializer.Deserialize(rdr); 
     rdr.Close(); 
    } 

    Assert.IsTrue(deserializedDt.negOffset == dt.negOffset); 
    Assert.IsTrue(deserializedDt.posOffset == dt.posOffset); 
    Assert.IsTrue(deserializedDt.gmtOffset == dt.gmtOffset); 
} 

这三个断言都会失败。

反序列化后,deserializedDt.negOffset是2014年7月9日晚上10点用的偏移-07:00(计算机的当前时区),deserializedDt.posOffset是2014年7月9日下午12点与的偏移-07:00,和deserializedDt.gmtOffset是7月9日2014下午5点,抵消-07:00。

在.Net 4.0项目中使用JSON.Net 8.0.3。

UPDATE ------------------

经过进一步调查,我在https://github.com/JamesNK/Newtonsoft.Json/issues/898

回答

1

这里是我我以前的答案,我结束了在定居后,来到第二的解决方案。

这一个的优点是你不必修饰DateTimeOffset或DateTimeOffset?的每个实例,它也可以在你不能控制和不能修饰的框架类上工作(比如Tuples )。它只是有用,没有任何额外的努力。

转换器被修改为保存为一个字符串,而不是分裂成一个对象:

public class DateTimeOffsetConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(DateTimeOffset) == objectType 
      || typeof(DateTimeOffset?) == objectType; 
    } 

    public override object ReadJson(
     JsonReader reader, 
     Type objectType, 
     object existingValue, 
     Newtonsoft.Json.JsonSerializer serializer) 
    { 
     if (reader.TokenType != JsonToken.String && reader.TokenType != JsonToken.Date) 
      return null; 

     DateTimeOffset dt; 

     // If you need to deserialize already-serialized DateTimeOffsets, 
     // it would come in as JsonToken.Date, uncomment to handle. Newly 
     // serialized values will come in as JsonToken.String. 
     //if (reader.TokenType == JsonToken.Date) 
     // return (DateTimeOffset)reader.Value; 

     var dateWithOffset = (String)reader.Value; 

     if (String.IsNullOrEmpty(dateWithOffset)) 
      return null; 

     if (DateTimeOffset.TryParseExact(dateWithOffset, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dt)) 
      return dt; 

     return null; 
    } 

    public override void WriteJson(
     JsonWriter writer, 
     object value, 
     Newtonsoft.Json.JsonSerializer serializer) 
    { 
     var dateTimeOffset = (DateTimeOffset)value; 

     // Serialize DateTimeOffset as round-trip formatted string 
     serializer.Serialize(writer, dateTimeOffset.ToString("O")); 
    } 
} 

需要一个定制ContractResolver注入转换器所需时:

public class DateTimeOffsetContractResolver: DefaultContractResolver 
{ 
    protected override JsonContract CreateContract(Type objectType) 
    { 
     var contract = base.CreateContract(objectType); 

     if (objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?) 
      contract.Converter = new DateTimeOffsetConverter(); 

     return contract; 
    } 
} 

并配置JSON.Net到使用您的ContractResolver:

var serializer = new JsonSerializer { 
    ContractResolver = new DateTimeOffsetContractResolver() 
}; 
+0

与使用BSON重复使用JSON的情况相同,何时他们会使用rn:http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx – mms

1

的BSON规范打开这个Github上的问题不允许存储DateTime的偏移量;自Unix时代以来的毫秒数。

如果你不想丢失偏移量,你可以创建一个JsonConverter,它将DateTime和Offset分开,以分别序列化(和反序列化)。例如:

public class DateTimeOffsetConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(DateTimeOffset) == objectType; 
    } 

    public override object ReadJson(
     JsonReader reader, 
     Type objectType, 
     object existingValue, 
     Newtonsoft.Json.JsonSerializer serializer) 
    { 
     if (reader.TokenType != JsonToken.StartObject) 
      return null; 

     reader.Read(); // PropertyName "DateTimeInTicks" 
     reader.Read(); // Property value 
     var ticks = (Int64)reader.Value; 

     reader.Read(); // PropertyName "Offset" 
     reader.Read(); // Property value 
     var offset = TimeSpan.Parse((String)reader.Value); 

     // Move forward to JsonToken.EndObject 
     reader.Read(); 

     return new DateTimeOffset(ticks, offset); 
    } 

    public override void WriteJson(
     JsonWriter writer, 
     object value, 
     Newtonsoft.Json.JsonSerializer serializer) 
    { 
     var dateTimeOffset = (DateTimeOffset)value; 

     var toSerialize = new { 
      DateTimeInTicks = dateTimeOffset.DateTime.Ticks, 
      Offset = dateTimeOffset.Offset 
     }; 

     serializer.Serialize(writer, toSerialize); 
    } 
} 

然后,您可以将其应用到你的类如下:

public class TestClass 
{ 
    public Int32 TestInt { get; set; } 

    [JsonConverter(typeof(DateTimeOffsetConverter))] 
    public DateTimeOffset TestDateTimeOffset { get; set; } 

    public String TestString { get; set; } 

    [JsonConverter(typeof(DateTimeOffsetConverter))] 
    public DateTimeOffset? TestNullableDateTimeOffset { get; set; } 
}