2016-04-29 83 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; } 
}