ra0o0f / arangoclient.net

ArangoDB .NET Client with LINQ support
Apache License 2.0
99 stars 37 forks source link

Json Serializer changes the format of ISO datetime strings #102

Open logiclabs opened 6 years ago

logiclabs commented 6 years ago

I'm using NodaTime for managing datetimes in my app, but have run into a problem with the way serialization is done in DocumentParser that prevents it from being used as a custom serialization converter.

There appears to be a longstanding issue with Json.Net about how it chooses to deserialize ISO datetimes (JamesNK/Newtonsoft.Json#862).

The issue is easiest to see when trying to load an object from an arango database that has an ISO date string valued property, and is being serialized into a .NET string property. Instead of the expected untouched ISO format (i.e. "2017-11-16T00:00:00Z"), the string is re-formatted using the standard DateTime string format (i.e. "11/16/2017 00:00:00").

The issue is that DocumentParser.ParseSingleResult uses a JsonReader which is automatically parsing the strings to datetimes. Unfortunately, JsonReader doesn't use the global arango serialization settings that specify to not do any date parsing. This settings needs to be manually applied to the reader i.e.

reader.DateParseHandling = DateParseHandling.None;
jObject = JObject.Load(reader);

var serializer = new DocumentSerializer(db).CreateJsonSerializer();
return jObject.ToObject<T>(serializer);

This seems like a quick fix though, as there are possibly other serialization settings being missed, and it might be better to change the usage of JObject.Load to an implementation that will apply the global serializer settings consistently.

In my case, this affects Nodatime's custom converters that are expecting the JToken being passed to it to be a String instead of a Date type. But I expect there may be cases where uses do not expect a date string to be changed

Test case to reproduce

   public class Person
    {
        public string BirthDate { get; set; }
        public string LastLogin { get; set; }
    }

    public class JsonSample
    {
        public static string SingleResult
        {
            get
            {
                return @"
{
'BirthDate':'1983-10-20',
'LastLogin':'2017-11-16T00:00:00Z',
'_id':'Person/KEY',
'_rev':'REV',
'_key':'KEY'
}
"; ;
            }
        }

       [Fact]
        public void ParseSingle()
        {
            using (var reader = GenerateReader(JsonSample.SingleResult))
            {
                var documentParser = new DocumentParser(new ArangoDatabase());
                JObject jObject = null;
                var person = documentParser.ParseSingleResult<Person>(reader, out jObject, true);

                Assert.Equal("1983-10-20", person.BirthDate); // PASS - Dates appear to be OK

                var lastLoginJToken = jObject["LastLogin"];
                Assert.Equal(JTokenType.String, lastLoginJToken.Type); // FAILS - Type is Date
                Assert.Equal("2017-11-16T00:00:00Z", person.LastLogin); // FAILS - Value is "11/16/2017 00:00:00"
            }
        }