Lately I’m working together with Daniela on a news feed reader for WP7 and one of its features is Google Reader synchronization. (It is called FeedTso, and if you are interested in knowing when it will be released I recommend you go and subscribe to the newsletter on the FeedTso site.)
I’ll talk about the Google Reader API in a future post (I’ll opensource the API when FeedTso is on the MarketPlace) but for the moment I want to discuss how I deserialize the JSON Date that I receive from the Google Reader API (and from what I’ve been told, Facebook API behaves the same) using Json.NET.
Json.NET has two converters to DateTime:
- JavaScriptDateTimeConverter, that converts from the standard javascript date format, new Date(1290774032435185)
- IsoDateTimeConverter, which converts dates from the ISO 8601 format (2010-11-26T12:53Z)
But unfortunately the Google Reader and Facebook APIs, instead of returning the date in one of the two aforementioned formats, they just return the number representing the unix time: 1290774032435185.
The easy solution would have been to just deserialize to an integer and then expose another property that converted the unix time timestamp to a DateTime, but I wanted something cleaner, so I decided to create a UnixDateTimeConverter for Json.NET.
I looked at the code of the original JavaScriptDateTimeConverter and I basically created mine removing all the parts that were parsing the “new Data(“ part of the value.
public class UnixDateTimeConverter: DateTimeConverterBase { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { Type t = ReflectionUtils.IsNullableType(objectType) ? Nullable.GetUnderlyingType(objectType) : objectType; if (reader.TokenType == JsonToken.Null) { if (!ReflectionUtils.IsNullableType(objectType)) { throw new Exception(String.Format("Cannot convert null value to {0}.", CultureInfo.InvariantCulture, new object[] { objectType })); } return null; } if (reader.TokenType != JsonToken.Integer) { throw new Exception(String.Format("Unexpected token parsing date. Expected Integer, got {0}.", CultureInfo.InvariantCulture, new object[] { reader.TokenType })); } long ticks = (long)reader.Value; DateTime d = ticks.ConvertFromUnixTimestamp(); if (t == typeof(DateTimeOffset)) { return new DateTimeOffset(d); } return d; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { long ticks = 0; if (value is DateTime) { ticks = ((DateTime)value).ToUniversalTime().ConvertToUnixTimestamp(); } else { if (!(value is DateTimeOffset)) { throw new Exception("Expected date object value."); } DateTimeOffset dateTimeOffset = (DateTimeOffset)value; ticks = dateTimeOffset.ToUniversalTime().UtcDateTime.ConvertToUnixTimestamp(); } writer.WriteValue(ticks); } }
I also had to implement a few utility functions because the original converter uses some private methods: a small helper to check whether a type is Nullable and an updated version of my UnixTime to DateTime conversion methods (now implemented as extension methods):
static class DateTimeUtils { public static DateTime ConvertFromUnixTimestamp(this long timestamp) { DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0); return origin.AddSeconds(timestamp); } public static long ConvertToUnixTimestamp(this DateTime date) { DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0); TimeSpan diff = date - origin; return (long)Math.Floor(diff.TotalSeconds); } }
This the first time I extended Json.NET and I’ve to say I’m impressed: the parsing is extremely well thought, no wonder why Json.NET is faster than the official DataContractJsonSerializer serializer.
In case you needed something similar, I packaged the 3 classes in zip file to make it easier to download.