Friday, April 6, 2012

ASP.net MVC - Handling json dates with a custom converter.

Handling dates in json can be a real pain as it has no concept of a date datatype - however by using the jQuery ajax() method and specifying a custom converter you can easily turn dates sent from the server into Date objects on the client.

A date when it comes down from the server in json format looks like this:

"BirthDate":"\/Date(1507546800000)\/"

 What is nice about this rather unfriendly format is that is has the /Date prefix (the backslashes are escape chars in json), this means we can look at a string and determine whether we should treat is as a date or not.

I typically use the jQuery ajax() method ($.ajax) to get a json payload from the server. By default if you specify a dataType of 'json' the ajax() method will use an inbuilt converter to turn the returned json string into a javascript object. Fortunately this method also takes a converters object which we can use to parse and transform the json before it is passed to the success() method.

In order for our converter to be called we need to specify a particular datatype - this cannot be one of the default in-built ones or it will be ignored  (such as json) - in this instance I've called mine jsonWithDates. When you specify your converter the first option is a string which says what you're converter is going to take in and return. You need to provide two values in the string separated by a a space (this feels a bit clumsy!). In this case I've specified "text jsonWithDates" because I specified that the dataType I want to deal with is "jsonWithDates" and I want "text" passed into my converter. When the response is received from the server jQuery will look at the converters and see that I have a converter that knows how to do the necessary conversion. Be warned that if you specify "json jsonWithDates" your converter will be passed a javascript object to convert and not a json string as jQuery will do a conversion for you implicitly, that's why I've used "text jsonWithDates".

Ok - so now we know how to hook into the pipeline and set up a converter, that's the hard part done! The second option in the converter object is a function that takes in the payload from the response and provides a converted response. It's up to you now to handle the transformation as you see fit. For my purposes I've used the JSON.parse() method which handily takes in a function that we can use to map individual values when parsing json. In my function I do a check to see if it is a date using a regular expression, if it passes I convert it to a javascript date object and return it.
 
 
Now in my success method I will receive a javascript object that has properties that are of type Date. The full code looks like the following:
 
function LoadDataFromServer() {
        $.ajax({
            url: GetGridURL(),
            type: 'GET',
            dataType: 'jsonWithDates',
            contentType: 'application/json:charset=utf-8',
            //add a custom converter to handle dates and the way 
            //they are sent back from the
            //server in .net
            converters: {
                "text jsonWithDates":
                function (jsonText) {
                    return JSON.parse(jsonText, function (key, value) {
                        // Check for the /Date(x)/ pattern
                        var match = /\/Date\((\d+)\)\//.exec(value);
                        if (match) {
                            var dateIn = new Date(parseInt(value.substr(6)));
                            return dateIn;
                        }
                        // Not a date, so return the original value
                        return value;
                    });
                }
            },
            success:
                function (data) { alert(data);  }
        });
    }
} 
Json date conversion - done!

3 comments:

  1. what about time zones?

    ReplyDelete
  2. How you handle the time zones is up to you. If you are dealing with multiple time zones one way to do it is to store the date times in the database on the server as UTC values - you will then need to check which time zone the user is in (you can do this on the server by checking the users browser settings or by having some user configuration which says which time zone they operate in) and adjusting the UTC date time accordingly before serializing it back to the client.

    ReplyDelete
  3. Thanks David, this has been very useful for me as my app uses a lot of dates/times. I made several adjustments which you may also find useful:

    1) Changed the regular expression to accept negative milliseconds (javascript dates before 1970)

    2) rather than using parseInt I use eval so as to pick up the time zone info

    3) made this a jQuery extension to make it easier to use

    Here is the result:

    jQuery.getJsonWithDates = function (url, data, callback) {
    return jQuery.ajax({
    url: url,
    type: 'GET',
    cache: false,
    data: data,
    dataType: 'jsonWithDates',
    contentType: 'application/json:charset=utf-8',
    //add a custom converter to handle dates and the way
    //they are sent back from the
    //server in .net
    converters: {
    "text jsonWithDates":
    function (jsonText) {
    return JSON.parse(jsonText, function (key, value) {
    // Check for the /Date(x)/ pattern
    var match = /\/Date\(-?(\d+)\)\//.exec(value),
    dateIn;
    if (match) {
    dateIn = eval("new " + value.replace(/[\\/]/g, ""))
    return dateIn;
    }
    // Not a date, so return the original value
    return value;
    });
    }
    },
    success: callback
    });
    };

    Then you can just call it like this:

    $.getJsonWithDates(myUrl, myData, myCallback);

    ReplyDelete