Custom DateTime JSON Format for .NET JavaScriptSerializer

Wednesday, December 28th, 2011 by Sebastian Markbåge

The DateTime serialization format in the built-in JSON serializer in .NET is particularly inconvenient as it requires a regular expression to parse and is not human-readable which makes debugging a pain.

ISO 8601

JSON doesn’t have a standard Date format per say. By design. Instead, the JavaScript defines toJSON methods so that objects can convert themselves to a JSON compatible format. The ECMAScript specification 5th Edition (15.9.5.4) defines that toJSON of a Date object should return a ISO 8601 formatted string in universal time (UTC). E.g: “2011-12-28T08:20:31.917Z”

This is a convenient format that specifically avoids confusion and ambiguity when passing it between systems. To turn it back into a Date object in JavaScript we can simply pass it to the Date constructor: E.g: Date(“2011-12-28T08:20:31.917Z”)

Custom Serialization Using JavaScriptConverter

.NET’s JavaScriptSerializer does provide an extension point for custom serialization. By calling RegisterConverters and passing custom JavaScriptConverter instances you can control how custom classes are serialized. APIs such as Script Services allow you to register such converters in Web.config. (AFAIK ASP.NET MVC doesn’t yet provide a hook to add JavaScriptConverters so you have to use a custom ActionResult.)

Unfortunately the JavaScriptConverter API will only allow you to convert an object into another object (IDictionary<string, object>). Not into a custom string format.

It is at this point you might be looking for other serializers such as Json.NET. There are certainly many much faster and less crappy serializers out there. However, there is a benefit to using the built-in one. Mainly it may be already integrated into existing system that you can’t change. Luckily, as long as they allow you to register custom JavaScriptConverters I have the solution for you.

The Hack

The implementation of JavaScriptSerializer does string serialization for several types but Uri in particular. Uri is a class instead of a struct and fortunately is not sealed. That means we can inherit from it to implement the IDictionary<string, object> interface. That will allowing us to pass it in our custom JavaScriptConverter. Since it inherits from Uri it will be serialized as a string. It will do some URI encoding but this doesn’t affect the ISO 8601 format.

Deserialization is handled by the default implementation.

You can also use this hack to serialize custom enum types as strings instead of numbers or objects.

The Code

 
public class DateTimeJavaScriptConverter : JavaScriptConverter
{
  public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
  {
    return new JavaScriptSerializer().ConvertToType(dictionary, type);
  }
 
  public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
  {
    if (!(obj is DateTime)) return null;
    return new CustomString(((DateTime)obj).ToUniversalTime().ToString("O"));
  }
 
  public override IEnumerable<Type> SupportedTypes
  {
    get { return new[] { typeof(DateTime) }; }
  }
 
  private class CustomString : Uri, IDictionary<string, object>
  {
    public CustomString(string str)
      : base(str, UriKind.Relative)
    {
    }
 
    void IDictionary<string, object>.Add(string key, object value)
    {
      throw new NotImplementedException();
    }
 
    bool IDictionary<string, object>.ContainsKey(string key)
    {
      throw new NotImplementedException();
    }
 
    ICollection<string> IDictionary<string, object>.Keys
    {
      get { throw new NotImplementedException(); }
    }
 
    bool IDictionary<string, object>.Remove(string key)
    {
      throw new NotImplementedException();
    }
 
    bool IDictionary<string, object>.TryGetValue(string key, out object value)
    {
      throw new NotImplementedException();
    }
 
    ICollection<object> IDictionary<string, object>.Values
    {
      get { throw new NotImplementedException(); }
    }
 
    object IDictionary<string, object>.this[string key]
    {
      get
      {
        throw new NotImplementedException();
      }
      set
      {
        throw new NotImplementedException();
      }
    }
 
    void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
    {
      throw new NotImplementedException();
    }
 
    void ICollection<KeyValuePair<string, object>>.Clear()
    {
      throw new NotImplementedException();
    }
 
    bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
    {
      throw new NotImplementedException();
    }
 
    void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
    {
      throw new NotImplementedException();
    }
 
    int ICollection<KeyValuePair<string, object>>.Count
    {
      get { throw new NotImplementedException(); }
    }
 
    bool ICollection<KeyValuePair<string, object>>.IsReadOnly
    {
      get { throw new NotImplementedException(); }
    }
 
    bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
    {
      throw new NotImplementedException();
    }
 
    IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
    {
      throw new NotImplementedException();
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
      throw new NotImplementedException();
    }
  }
}

9 Responses to “Custom DateTime JSON Format for .NET JavaScriptSerializer”

  1. kurt.andersson Says:

    Hi
    You can create an anonymous type as a variation by using ToList()
    regards
    Kurt
    var v = db.Orders.OrderBy(c => c.OrderID).Skip(i).Take(10).ToList().Select(c => new {Kundid= c.CustomerID,Säljare= c.EmployeeID,Orderdatum=((DateTime) c.OrderDate).ToShortDateString()});

  2. web hosting Says:

    web hosting…

    [...]Custom DateTime JSON Format for .NET JavaScriptSerializer – Calyptus Life[...]…

  3. http://www.cozylilyswimwear.com Says:

    I’m not sure where you are getting your information, but great topic. I needs to spend some time learning more or understanding more. Thanks for great info I was looking for this information for my mission.

  4. Mindaugas Says:

    could you please explain how to use this class? thanks indeed for your help. you doing brilliant job here.

  5. Mindaugas Says:

    and is it possible to get PropertyInfo in there? :) thanks a lot

  6. Hack of the day: Defeating C#’s JavaScriptSerializer | Tipbit Says:

    [...] the same subclass, and all just so that he can print a DateTime as a string.  Incredible!  See Sebastian Markbåge’s post for all the gory [...]

  7. 列车时刻表 Says:

    分析的很透彻,很欣赏你的看法,学习了。

  8. health food Says:

    t need to go to super market, a recipe book or a take-away
    restaurant all you get at your home at a fraction of the
    cost. Most likely, other members experience the same health issue especially
    if it is learned to be contagious. Juice Fast is a fasting method and a
    detox diet where a person consumes only fruit and vegetable juices to obtain nutrition while otherwise abstaining
    from food consumption.

  9. Eileen Says:

    Rich in linoleic acid, vitamin E and many other
    essential fatty oils, it is especially effective for the repairing skin around the eyes and is known
    to visibly reduce the appearance of fine lines and wrinkles.
    There are real natural face moisturizers on the market; you just have to know what to look for.
    These are collagen and elastin, which are responsible in maintaining firmness and elasticity.

Leave a Reply