Search code examples
c#datetimejson.netdeserialization

json.net: DateTimeStringConverter gets an already DateTime converted object in ReadJson()


Prerequisites:

  • JSON.Net 11.0.2

I need to store the UTC DateTime round-trip date/time pattern via a JSON based REST-API.

string utcTimestamp = DateTime.UtcNow.ToString( "o" );
// 2018-11-27T22:35:32.1234567Z

So I wrote myself a DateTimeStringConverter to ensure no local culture information gets involved.

class DateTimeStringConverter:
    JsonConverter<DateTime>
{
    public override void WriteJson( JsonWriter writer, DateTime value, JsonSerializer serializer )
    {
        string convertedValue = value.ToString( "o" );
        writer.WriteValue( convertedValue );
    }

    public override DateTime ReadJson( JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer )
    {
        string value = reader.Value.ToString( );
        DateTime convertedValue = DateTime.Parse( value ).ToLocalTime( );
        return convertedValue;
    }
}

I was very confused as to why I was getting a DateTime object without the milliseconds. After a lot of trial and error, I got into it.

public override DateTime ReadJson( JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer )
{
    Console.WriteLine( reader.Value.GetType( ).ToString( ) );
    // System.DateTime

    string value = reader.Value.ToString( );
    DateTime convertedValue = DateTime.Parse( value ).ToLocalTime( );
    return convertedValue;
}

JSON.Net serves me an already converted DateTime object to deserialize on my own without any millisecond data. I didn't find any clues as to whether it is a bug or a feature.

To counter check it I wrote a BooleanStringConverter.

class BoolStringConverter:
    JsonConverter<bool>
{
    public override void WriteJson( JsonWriter writer, bool value, JsonSerializer serializer )
    {
        string convertedValue = false == value ? "False" : "True";
        writer.WriteValue( convertedValue );
    }

    public override bool ReadJson( JsonReader reader, Type objectType, bool existingValue, bool hasExistingValue, JsonSerializer serializer )
    {
        Console.WriteLine( reader.Value.GetType( ).ToString( ) );
        // System.String

        string value = ( string ) reader.Value;
        bool convertedValue = "False" == value ? false : true;
        return convertedValue;
    }
}

JSON.Net doesn't serve me an already converted bool object.

Is it a bug or is it a feature?


Solution

  • This is a known behavior of Json.Net. Since JSON does not have a built-in syntax for denoting dates (like it does for booleans), they have to be represented as strings. By default Json.Net tries to be nice and parse date-looking strings for you.

    If you are using your own converter for dates, or otherwise want to handle date parsing yourself, you need to be sure to set the DateParseHandling setting to None in the JsonSerializerSettings, otherwise the internal reader will try to handle it first.

    JsonSerializerSettings settings = new JsonSerializerSettings
    {
        Converters = new List<JsonConverter> { new DateTimeStringConverter() },
        DateParseHandling = DateParseHandling.None
    };
    
    var foo = JsonConvert.DeserializeObject<Foo>(json, settings);