This post is over 6 months old. Some details, especially technical, may have changed.

Better JSON Serialisation for ASP.NET MVC

The Json() method of controllers is a nice little convenience method for serialising server side data into JSON for handy consumption on the client side but it's not without it's issues.  Firstly it just cant serialise dates in any nice way due to no hard and fast standard in JSON for dates.

return Json(new
{
    SomeDate = DateTime.Now
});

Simply gets spat back to the front end as a string like so,

"/Date(1290181373164)/"

Not exactly the most useful thing in the world.  You are going to need to make use of JSON's reviver callback to parse this accordingly.

Another issue is lack of configurability.  C# coding standards recommend the use of PascalCased property names whereas in JavaScript land the standard is camelCase (and many frameworks assume this casing which can cause issues behind the scenes *cough*ExtJS*cough*).  You can fix this by adding DataContract and DataMember attributes to your model object but it gets a bit messy and open to error if an attribute is left out accidentally.  Or what if you don't have access to the model code?

JSON.NET

Json.NET is my JSON de/serialiser of choice.  It's fast and widely configurable plus it fixes the problems I've mentioned above (among many many other things).  Not to mention some BSON (Binary JSON) support which could come in handy in the future.

JsonNetResult

So lets fix things.  Lets wrap Json.NET up in an ActionResult type and offer a configurable entry point.

/// <summary> 
/// Simple Json Result that implements the Json.NET serialiser offering more versatile serialisation 
/// </summary> 
public class JsonNetResult : ActionResult
{
    public JsonNetResult()
    {
    }

    public JsonNetResult (object responseBody)
    {
        ResponseBody = responseBody;
    }

    public JsonNetResult(object responseBody, JsonSerializerSettings settings)
    {
        Settings = settings;
    }

    /// <summary>Gets or sets the serialiser settings</summary> 
    public JsonSerializerSettings Settings { get; set; }

    /// <summary>Gets or sets the encoding of the response</summary> 
    public Encoding ContentEncoding { get; set; }

    /// <summary>Gets or sets the content type for the response</summary> 
    public string ContentType { get; set; }

    /// <summary>Gets or sets the body of the response</summary> 
    public object ResponseBody { get; set; }

    /// <summary>Gets the formatting types depending on whether we are in debug mode</summary> 
    private Formatting Formatting
    {
        get
        {
            return Debugger.IsAttached ? Formatting.Indented : Formatting.None;
        }
    }

    /// <summary> 
    /// Serialises the response and writes it out to the response object 
    /// </summary> 
    /// <param name="context">The execution context</param> 
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;

        // set content type 
        if (!string.IsNullOrEmpty(ContentType))
        {
            response.ContentType = ContentType;
        }
        else
        {
            response.ContentType = "application/json";
        }

        // set content encoding 
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }

        if (ResponseBody != null)
        {
            response.Write(JsonConvert.SerializeObject(ResponseBody, Formatting, Settings));
        }
    }
}

As you can see I have exposed the JsonSerializerSettings object allow developers to tune the serialisation all they want.  We can also make things even simpler by providing a Controller extension method that we can call without having to create the object directly,

public static class ControllerExtensions
{
    public static JsonNetResult JsonEx(this Controller controller, object responseBody)
    {
        return new JsonNetResult(responseBody);
    }

    public static JsonNetResult JsonEx(this Controller controller, object responseBody, JsonSerializerSettings settings)
    {
        return new JsonNetResult(responseBody, settings);
    }
}

Simple stuff but still pretty powerful, lets see it in action.

The Date Problem

We can fix the date problem in a number of ways via converters.  Converters give us a custom way to convert values of objects and Json.NET provides a number of converters out of the box - IsoDateTimeConverter, JavaScriptDateTimeConverter and an abstract base DateTimeConverter to roll your own.  Lets use the JavaScriptDateTimeConverter for this example,

var result = new
{
    SomeDate = DateTime.Now
};

return this.JsonEx(result, new JsonSerializerSettings()
{
    Converters = new List<JsonConverter>
    {
        new JavaScriptDateTimeConverter()
    }
});

The resultant JSON object will generate a JSON object with a JavaScript Date constructor

{
    new Date(?)
}

While not strictly valid JSON native JSON parsers and JSON2 handle it fine.

The Casing Issue

We can use Json.NET's contract resolver to automagically convert PascalCased property names of C# to camelCased property names of JavaScript.

var result = new
{
    SomeProperty = ?my value?
};

return this.JsonEx(result, new JsonSerializerSettings()
{
    ContractResolver = new CamelCasePropertyNamesContractResolver()
});

Which gives us a response like so,

{
    someProperty: ?my value?
}

Fixed.

Wrap Up

So we have managed to create a much more powerful JSON serialisation technique without having to sacrifice too much of the convenience of the Json() method in the controller.  Anyone got any recommendations/enhancements they are willing to share?  

Published in .NET on November 20, 2010