2014-11-27 18 views
6

In regulärem .net, Wenn wir eine Zeit haben, die DateTimeKind.Unspecified Wenn wir ToLocal konvertieren - es nimmt an, das Eingangsdatum ist UTC bei der Konvertierung. Wenn wir ToUniversal konvertieren - nimmt es an, das Eingabedatum ist lokal beim Konvertierenjson.net serialisierung/deserialisierung von datetime 'unspezifiziert'

Wenn jedoch in JSON.Net unser String-Datum in JSON.Net nicht angegeben ist, scheint es nicht diese Logik zu haben? Sehen Sie sich meine Testfälle an - mache ich etwas falsch? Oder ist das Absicht? oder ein Bug in JSON.Net? Danke!

// TODO: This Fails with output 
    //  date string: "2014-06-02T21:00:00.0000000" 
    //  date serialized: 2014-06-02T21:00:00.0000000Z 
    //  Expected date and time to be <2014-06-03 04:00:00>, but found <2014-06-02 21:00:00>. 
    [TestMethod] 
    public void NEW_Should_deserialize_unspecified_datestring_to_utc_date() 
    { 
     string dateString = "\"2014-06-02T21:00:00.0000000\""; 
     DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 
     DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc); 
     dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime()); 

     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; 
     settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
     DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);     

     Console.WriteLine("date string: " + dateString); 
     Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 

     dateSerialized.Kind.Should().Be(DateTimeKind.Utc); 
     dateSerialized.Should().Be(dateRaw.ToUniversalTime()); 
     dateSerialized.Should().Be(dateRawAsUtc); 
    } 

    // TODO: This Fails with output 
    //  date string: "2014-06-02T21:00:00.0000000" 
    //  date serialized: 2014-06-02T21:00:00.0000000-07:00 
    //  Expected date and time to be <2014-06-02 14:00:00>, but found <2014-06-02 21:00:00>. 
    [TestMethod] 
    public void NEW_Should_deserialize_unspecified_datestring_to_local_date() 
    { 
     string dateString = "\"2014-06-02T21:00:00.0000000\""; 
     DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 
     DateTime dateRawAsLocal = new DateTime(2014, 6, 2, 14, 0, 0, 0, DateTimeKind.Local); 
     dateRawAsLocal.Should().Be(dateRaw.ToLocalTime()); 

     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.DateTimeZoneHandling = DateTimeZoneHandling.Local; 
     settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
     DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings); 

     Console.WriteLine("date string: " + dateString); 
     Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 

     dateSerialized.Kind.Should().Be(DateTimeKind.Local); 
     dateSerialized.Should().Be(dateRaw.ToLocalTime()); 
     dateSerialized.Should().Be(dateRawAsLocal); 
    } 

    [TestMethod] 
    public void NEW_Should_deserialize_unspecified_datestring_to_unspecified_date() 
    { 
     string dateString = "\"2014-06-02T21:00:00.0000000\""; // unspecified, does not have the 'Z' 
     DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 

     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified; 
     settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
     DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);     

     Console.WriteLine("date string: " + dateString); 
     Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 
     dateSerialized.Kind.Should().Be(DateTimeKind.Unspecified); 
     dateSerialized.Should().Be(dateRaw); 
    } 
+0

Versuchen zu verwenden 'DateTimeOffset' statt 'DateTime'. Es sollte korrekt deserialisiert werden. – daramasala

Antwort

2

Ich bin nicht 100% sicher, was Sie hier suchen, aber ich denke, es ist nicht sicher davon ausgehen, dass JSON.Net alle Ihre Bedürfnisse, ohne ein wenig Hilfe treffen. Als Mr. Newton says:

Daten in JSON sind schwer.

Das erste, was ist zu bestimmen, ob oder Sie wollen nicht spezifiziert Daten unterstützen die Annahme oder ob Sie gehen davon aus, dass alle eingehenden Daten universell sind, auch wenn sie die Hinter Z.

Wenn Sie fehlen gehen davon aus, dass alle eingehenden Daten universell sind, können Sie nur sehen, wenn sie eine Hinter Z und, wenn nicht, fügen Sie es (nicht genau Produktion Code, aber Sie bekommen die Idee):

if (!dateString.EndsWith("Z\"", StringComparison.InvariantCultureIgnoreCase)) 
{ 
    dateString = dateString.Substring(0, dateString.LastIndexOf("\"", StringComparison.InvariantCultureIgnoreCase)) + "Z\""; 
} 

Diese Änderung in der Annahme, tut verlangen, dass die Daten, für die Sie testen, geändert werden müssen, um Utc zu sein.

Wenn Sie nicht wollen, dass eingehende Daten universell sind zu übernehmen, sondern sie als nicht spezifiziert zu behandeln, müssen Sie die Art und Weise ändern, dass Sie die eingehenden JSON konvertieren durch Ersetzen:

JsonSerializerSettings settings = new JsonSerializerSettings(); 
settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; 
settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);     

mit:

var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter(); 
DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter); 

Dies führt zu einem unbestimmten Datum, das exakt mit dem dateString übereinstimmt. Hier ist, wo Sie Ihre helfende Hand ins Spiel kommt:

if (dateSerialized.Kind == DateTimeKind.Unspecified) 
{ 
    dateSerialized = dateSerialized.ToUniversalTime(); 
} 

Das bedeutet, dass der vollständige, überarbeitete erste Test wie folgt aussehen und es wird passieren:

string dateString = "\"2014-06-02T21:00:00.0000000\""; 
    DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); 
    DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc); 
    dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime()); 

    var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter(); 
    DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter); 
    if (dateSerialized.Kind == DateTimeKind.Unspecified) 
    { 
     dateSerialized = dateSerialized.ToUniversalTime(); 
    } 

    Console.WriteLine("date string: " + dateString); 
    Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); 

    dateSerialized.Kind.Should().Be(DateTimeKind.Utc); 
    dateSerialized.Should().Be(dateRaw.ToUniversalTime()); 
    dateSerialized.Should().Be(dateRawAsUtc); 
+0

UGH! ... aber auf jeden Fall danke für das Post/Info ... klingt wie dies ist "von Design" ... Nun, wie für den Kommentar über die Unterstützung unspezifiziert, passiert dies oft - z. ein Entwickler nur myDate = new DateTime (2014, 10, 23); Ich möchte vorhersehbares Verhalten darüber haben, was das bedeutet oder passiert, und wenn .Net es in einer Weise macht, und JSON.Net macht es anders, das ist ... hässlich ... und übrigens, ich habe ServiceStack vorher benutzt, und sie machen es genauso wie .Net. – Raymond

Verwandte Themen