Case study: custom json converter for DateTime

Kiwi.Json uses ISO-8601 for representing encoded DateTime values. This means that encoded data is human readable as in the following json:

{
sortableDate: "2012-03-25T16:01:26",
universalySortableDate: "2012-03-25 16:01:26Z"
}

Microsoft chose another path in their Json implementations. In short, they use an encoding based on some javascript text encoding quirks + a milliseconds offset from a the fixed date.
So, the date above would occur in json as

{
 msDate: "\/Date(1332691286000)\/"
}

Not very readable, but, there are certains aspects making this and similar solutions quite sound.

So, how do we face json encoded not in ISO-8601 but in the Microsoft format? Out of the box, Kiwi.Json fails (actually throws a parsing exception), but there is a solution.
Specify a custom DateTime formatter, AspNetDateTimeConverter, either globally as in

JsonConvert.RegisterCustomConverters(new AspNetDateTimeConverter());
..
var dataWithDateTime = JsonConvert.Parse<MyTypeWithDateTime>(jsonEncoding)

or, per call to Parse() as in

var dataWithDateTime = JsonConvert.Parse<MyTypeWithDateTime>(jsonEncoding, new AspNetDateTimeConverter())

AspNetDateTimeConverter was quite easy to implement. The full code is

public class AspNetDateTimeConverter : AbstractJsonConverter
{
  private static readonly DateTime BaseJavaScriptDate = new DateTime(1970, 1, 1);

  public override ITypeBuilder CreateTypeBuilder(Type type)
  {
    return TryCreateTypeBuilder<DateTime, string>(type, ParseDate)
           ?? TryCreateTypeBuilder<DateTime?, string>(type, s => ParseDate(s));
  }

  public override ITypeWriter CreateTypeWriter(Type type)
  {
    return TryCreateWriter<DateTime>(type,
        dt => new JsonLiteralContent(string.Concat(@"""\/Date(",(dt - BaseJavaScriptDate).TotalMilliseconds,@")\/""")));
  }

  private DateTime ParseDate(string s)
  {
    return BaseJavaScriptDate.Add(TimeSpan.FromMilliseconds(long.Parse(s.Substring(6, s.IndexOf(')') - 6))));
  }
}

If we dissect this little animal, the vital organs are

private static readonly DateTime BaseJavaScriptDate = new DateTime(1970, 1, 1);

which denotes the era start used in javascript.

Writing of dates are controlled by

public override ITypeWriter CreateTypeWriter(Type type)
{
  return TryCreateWriter<DateTime>(type,
    dt => new JsonLiteralContent(string.Concat(@"""\/Date(",(dt - BaseJavaScriptDate).TotalMilliseconds,@")\/""")));
}

This code simply says, “if a DateTime value comes in for serialization, I can take it and I return a literal string instead formatted as “\/Date(<milliseonds since era>)\/”.

The utility method ParseDate() is

private DateTime ParseDate(string s)
{
  return BaseJavaScriptDate.Add(
    TimeSpan.FromMilliseconds(long.Parse(s.Substring(6, s.IndexOf(')') - 6))));
}

No error checking and stuff, since we know what we are doing. Just peek past “\/Date(” and fetch the milliseconds and add them to the era start.

The method CreatetypeBuilder() is more enigmatic.

public override ITypeBuilder CreateTypeBuilder(Type type)
{
  return TryCreateTypeBuilder<DateTime, string>(type, ParseDate)
         ?? TryCreateTypeBuilder<DateTime?, string>(type, s => ParseDate(s));
}

What does it do? Well, it says it can handle both DateTime and Nullable<DataTime> aka DateTime? at the same time. In fact, the above pattern of
TryCreateTypeBuilder<T,> ?? TryCreateTypeBuilder<T?,> makes great sense for valuetypes, since then a custom converter can handle their nullable counterparts as well.

This is a post in my series about a practical Json implementation in .NET. The source actual code is hosted on github (https://github.com/jlarsson/Kiwi.Json). A compiled version is avaiable from nuget (http://nuget.org/packages/Kiwi.Json).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s