2016-04-29 8 views
1

BSONにシリアル化するためにJSON.Netを使用しようとしていますが、元のオフセットは尊重されているようです。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); 
} 

3つのアサーションはすべて失敗します。 deserializedDt.posOffsetは2014年12 PM -07のオフセットで7月9日で、00(コンピュータの現在のタイムゾーン):逆シリアル化後

deserializedDt.negOffsetは午後10時-07のオフセットで7月9日2014で00、およびdeserializedDt.gmtOffsetは7月9日です2014 5 PM - オフセットは-07:00です。

.Net 4.0プロジェクトでJSON.Net 8.0.3を使用する。

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

はさらに調査した後、私はhttps://github.com/JamesNK/Newtonsoft.Json/issues/898

答えて

1

をここで私は上のセトリング終わった私の前の回答の後に来た第2の解決策はあります。

DateTimeOffsetまたはDateTimeOffset?の各インスタンスをデコレートする必要はありません。また、制御しないで飾ることができないフレームワーククラス(タプル)。それは何の追加努力もなく、ちょうど動作します。

コンバータは、文字列の代わりに、オブジェクトに分割するとして保存するように変更されます。

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

とTO 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のオフセットを格納することはできません。 it stores UTC datetime as an Int64、Unixエポックからのミリ秒。

オフセットを失いたくない場合は、OffsetからDateTimeを分離して別々にシリアル化(およびデシリアライズ)するJsonConverterを作成できます。たとえば、次のように

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; } 
} 
関連する問題