Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Native JSON Parsing: What does it mean?


:P
On this page:

JSON support is part of the EcmaScript 3.1 standard and so is likely to become ubiquitous in all browsers going forward. Internet Explorer 8 now includes native JSON parsing. FireFox 3.5 and the next version of WebKit/Safari too have or shortly will have native JSON support inside of the browser. This is good news as it helps simplify a common task for AJAX applications that send data back and forth between clients.

You can check out the native JSON support today:

Internet Explorer 8

FireFox 3.1 Beta 3 (new FF 3.5 Beta 4 expected next week)

Native JSON Support finally – sort of

IE 8 is the first shipping browser with native JSON support. Quick tests confirm that the performance gain from using native JSON serialization is significantly faster than using the json2 JavaScript library the specification of which the JSON object support in browsers and EcmaScript are based on. FireFox 3.5 will also have JSON object to handle JSON when it ships. WebKit also has an open ticket for JSON object support, so it looks like in the near future most current browsers at least will have native JSON support.

The native JSON object is based on Douglas Crockford’s JSON2 interface, which provides .stringify() and .parse() methods to encode and decode JSON data respectively. It’s nice that JSON2’s interface was decided upon given that json2 has become one of the more popular JSON client implementations. The native browser JSON parsers are basically a drop in replacement – there’s nothing to do, you just get the improved performance without code changes. The JSON2 implementation includes checks to see if a JSON  object exists already and if not leaves it in place so the native version is used. The JSON2 implementation also includes .toJSON() methods for the elementary JavaScript types (string, number, boolean, date) which are also implemented by the native parsers.

So, using either Crockford’s json2.js or the native JSON object you can do:

var inst = { entered: new Date(2009,1,1,10,10),
name: "Rick", company: "West Wind Technologies", address: { street: "32 Kaiea Place", city: "Paia", zip: "96779" }, visits: 100 };
var json = JSON.stringify(inst);
alert(json);

var inst2 = JSON.parse(json);

assert(inst2.name == inst.name && inst2.accesses == inst.accesses);

Those pesky Dates – Still!

One prickly issue with JSON parsing in general is date parsing and serialization. In fact if you look at the example above and print inst2.entered you will find that rather than a date you still get a string. JavaScript doesn’t have a date literal which means there’s not been an official way to represent a date literal value in JSON. json2 and the native JSON parsers encode dates in an ISO string format:

ISO Encoded Date

"2009-01-03T18:10:00Z"

All the native implementations follow this format as does json2.js.

Unfortunately the JSON.parse() method does NOT actually convert these ISO date strings back into date objects. So the following code is not going to provide what you might expect:

var date = new Date(2009, 0, 1, 10, 55);
var json = JSON.stringify(date);
alert(json);

var date2 = JSON.parse(json);
alert(date2 + "\r\n" + typeof date2);

You’ll end up with a date string in ISO format for the latter parsed code.

In the end the new native JSON parsers don’t help any with date conversion. In order to fix up the date value you can use a ‘reviver’ function as the second parameter on .parse(). It’s easy enough to do with some RegEx code, but it’s not exactly intutive and definitely something that you’d want to stick into a library function.

I like to extend the JSON object – either a native one or the json2 object (make sure this is called after JSON has been loaded) – by adding a couple of methods to do the date conversions since this keeps the logic in the context of JSON conversion:

    var reISO = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;
    var reMsAjax = /^\/Date\((d|-|.*)\)\/$/;

    JSON.parseWithDate = function(json) {
        return JSON.parse(json,
                function(key, value) {
                    if (typeof value === 'string') {                       
                        var a = reISO.exec(value);
                        if (a)
                            return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));                                                
                        a = reMsAjax.exec(value);
                        if (a) {
                            var b = a[1].split(/[-,.]/);
                            return new Date(+b[0]);
                        }
                    }
                    return value;
                });
    };
    JSON.dateStringToDate = function(dtString) {
        var a = reISO.exec(dtString);
        if (a)
            return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
        a = reMsAjax.exec(dtString);
        if (a) {
            var b = a[1].split(/[-,.]/);
            return new Date(+b[0]);
        }
        return null;
    };

The reviver function runs through the object structure after the JSON parsing is complete and gives the function a chance to ‘fix up’ any of the values. In this case we’d have to look for a string and then match the regEx expression. parseWithDate() handles both ISO and the Microsoft AJAX string date format.

As you can imagine though – this process is slow even if you are using the native JSON parser as every element in the result set is iterated over and every string compared to a RegEx expression. Definitely sub-optimal. In a few quick tests with the simple object above I found that adding the reviver nearly doubled the time it took to process the JSON parsing.

I suspect the reason for not natively turning the date string into a native date is that there is in effect no date literal and the above leaves room for future changes in JavaScript maybe one of these lifetimes of coming up with a date literal :-}.

Since there is a fairly big perf hit for the reviver I also added a dateStringToDate function, which can be used after you’ve called parsed and gives you more control over when the conversion actually occurs. This is more efficient because you only apply the checks and actual conversions against fields that you know are dates. But of course it’s a bit less natural to work with:

var date = new Date(2009, 0, 1, 11, 44);
var inst = { entered: date,
    name: "rick",
    company: "west wind technologies",
    address: { street: "32 kaiea place", city: "paia", zip: "96779" },
    visits: 100
};

var json = JSON.stringify(inst);
var inst2 = JSON.parse(json);

alert( JSON.stringToDate(inst2.entered) );

Experimenting with Native JSON

If you want to experiment around with this and you have either IE 8 or FireFox 3.1/5 installed you can run the following script code to compare performance of native vs. json2.js behavior:

   <script id="scriptJson" src="scripts/json2.js" type="text/javascript"></script>
   <script type="text/javascript">
            var iterations = 10000;
            var date = new Date(2009, 1, 1, 10, 10);

            var inst = { entered: date,
                name: "Rick",
                company: "West Wind Technologies",
                address: { street: "32 Kaiea Place", city: "Paia", zip: "96779" },
                visits: 100
            };

            var time = new Date().getTime();
            for (var i = 0; i < iterations; i++) {
                var json = JSON.stringify(inst);
            }
            var time = new Date().getTime() - time;

            var time2 = new Date().getTime();
            var inst2 = null;
            for (var i = 0; i < iterations; i++) {
                inst2 = JSON.parse(json);
            }
            time2 = new Date().getTime() - time2;
            var time3 = new Date().getTime();
            var inst3 = null;
            for (var i = 0; i < iterations; i++) {
                inst3 = parseWithDate(json);
            }
            time3 = new Date().getTime() - time3;


            alert("stringify: " + time + "\r\n" +
              json + "\r\n" +
              "parse: " + time2 + "\r\n" +
              inst2.entered + "\r\n" +
              "parse: " + time3 + "\r\n" +
              inst3.entered
              );
               
        function parseWithDate(json)
        {
            return JSON.parse(json,
                function(key, value) {
                    if (typeof value === 'string') {
                        var a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                        if (a)
                           value = new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
                    }
                    return value;
            });
        }
  </script>

The easiest way to test and compare native vs. javascript versions is using IE 8 as standards mode supports the native JSON object and compatibility view doesn’t. So you can simply click on the ‘broken’ page icon to toggle between standards and compatibility mode:

image 

If you run these examples and compare the json2.js performance vs. native JSON support you’ll find that performance can be drastically faster in the native parsers. IE 8’s stringify in particular is over 10 times faster than the json2 code (in compatibility mode). FireFox is more moderate with 2-3 times improvements for stringify although it’s much faster in JavaScript in the first place. parsing too is about 2-3 times faster natively in both browsers. Adding in the reviver adds a little more to the perf gain vs. the code version.

In IE 8 it’s easy to do this by switching in and out of compatibility view. For FireFox you’ll need to have side by side instances of FireFox installed. The code also demonstrates the perf difference between parsing the date and leaving it it simply as a string which is significant (2 times slower regardless of native parser or json2.js).

What about Microsoft’s AJAX Stack?

Native JSON support initially won’t have any effect on the Microsoft client stack, mainly because Microsoft has a few custom formats it uses in JSON formatting. Most notably that would be the date format we already discussed. MS Ajax uses the following format:

"\/Date(1230807660000-1000)\/"

The good news is that the latest versions of ASMX and WCF REST JSON services accept dates in the ISO format that the native JSON parsers and json2 use so for sending data to the server from a non-Microsoft client nothing else needs to be done for date fix up.

However, if you plan on consuming dates from the server, you will need to convert those Microsoft format dates – the JSON.parseWithDate() and JSON.dateStringToDate() functions I showed earlier both support this date format so you can use that code.

Of course the point is moot if you’re using the Microsoft AJAX Client Library since it handles the conversions automatically. It’ll be interesting to see if the MS AJAX client libraries will take advantage of the native JSON parsers in the future.

What about you?

If you’ve been using jQuery or other non-MS AJAX Client Library, how have you been dealing with JSON Serialization/Deserialization? How have you managed the date issues? I’ve always had customized solutions for this because I certainly don’t want to have to think about doing data ‘fix-ups’ in my application level client code so it’s always been transparent for me.

For me JSON serialization has been an ongoing saga over the years as I’ve switched my client library between parsers several times. To handle the native parsers I finally  switched  to the json2/native JSON implementation with the extensions I posted above. Effectively though I rarely use the serializer directly – the behavior of callbacks is all wrapped up in my client library so there are no code changes regardless of parser used.

Related Posts

JSON and Date Embedding
jQuery AJAX calls to WCF REST Service
Using jQuery with ASP.NET

Posted in JavaScript  AJAX  

The Voices of Reason


 

Jason Haley
April 20, 2009

# Interesting Finds: April 20, 2009

Interesting Finds: April 20, 2009

Andrew D
April 21, 2009

# re: Native JSON Parsing: What does it mean?

Useful post. Good work fella.

Luke Smith
May 11, 2009

# On the right track

Upcoming releases of the YUI library will leverage native JSON for parsing when it's available. From my testing, IE8 did a great job implementing the spec. I only found two exceptions in stringify:
1) input field.value is stringified to "null" if unset or empty (which was a show stopper for integration)
2) it incorrectly implements the third argument (space - used for formatting the output) for the empty string and values above 100. This wasn't such a big deal, but eh

FF3.5 on the other hand has further to go at last test:
1) it doesn't (yet) implement the additional arguments to parse(data, *reviver*) or stringify(data, *replacer*, *space*)
2) at last test, it produced incorrect output when a function was present in the data (zilla says this bug has been fixed on the dev tree though)
3) it doesn't parse('"string or other literals not in an object"') as the spec states it should
4) it stringifies Date instances to an extra point of precision
5) parse is more forgiving than the spec defines, but in non-critical ways
6) maybe I've forgotten something else...

All in all, really good progress, to be sure. I'm confident FF3.5's implementation will be more complete and stable by release and expect more JS libs will adopt leveraging native functionality when available (and not broken).

Rick Strahl
May 11, 2009

# re: Native JSON Parsing: What does it mean?

@Luke - Thanks for the detailed update. Reviver is definitely working for me in FF 3.5 - I use it to fix up dates and that works (or at least it did in Beta 3 - need to double check B4).

Luke Smith
May 11, 2009

# re: Native JSON Parsing: What does it mean?

Oh good! I guess I haven't tested since b2. Definitely worth another look then.

KierenH
May 18, 2009

# re: Native JSON Parsing: What does it mean?

Rick,
You have previously blogged about a modified JSON2. Is extending JSON/JSON2 now you're preferred solution?
Cheers, Kieren

Rick Strahl
May 18, 2009

# re: Native JSON Parsing: What does it mean?

@Kieren - Yes I actually changed my code in my library to include JSON2 and extend it with the Reviver for date processing. I think this is the right thing to do given that JSON2 is compatible with the native JSON parsers. For compatibility's sake my extensions also expose the old methods I used previously.

Luigi Gaeta
May 19, 2009

# re: Native JSON Parsing: What does it mean?

How did you solve the "null" parsing of an empty string? I was using the json2.js library and till IE7 (and Mozilla and Opera and Safari) everything goes right (string empty stringified in string empty!). What's wrong in IE8 JSON implementation?
Any suggestion to solve this problem (I've a tonn of code written in javascript with JSON.stringify() everything!).
Cheers, Luigi

David
January 06, 2010

# re: Native JSON Parsing: What does it mean?

I don't know if anyone else had problems with this implementation but I'm trying to get this working with the json2.js file (version 2009-09-29). And it returns NaN's instead of dates.

I had to change the following code:
return new Date(+b[0]);


to:
return new Date(parseInt(b[0]));


The piece "+b[0]" didn't work out for me...

I'm located to the right of GMT, my time offset is +1h. So I don't know if it has anything to do with this (didn't have time to test offset correction, but running server in my timezone & webclient works and that's enough for me :) ).

sky sanders
February 24, 2010

# re: Native JSON Parsing: What does it mean?

Actually, it seems that FF JSON is not quite api compatible as you may or may not have noticed...

When a replacer is used the value returned should be serialized. This works as expected in json2.js and IE8 but FF is apparently ignoring the return value and serializes the key.

I think this is the motivation, in stringifyWcf, for modifying the object being serialized. Serialization should not present (undocumented) side effects like this, in my opinion.

You might notice that if you force json2.js as your JSON implementation that stringifyWcf will work just fine in all browsers even when you comment out the 'this[key] = val;' line.

My plan, when dealing with WCF endpoints, is to force json2, use the modified stringifyWcf and parseWithDate and accept the performance hit.

Also, I am not sure if an 'Ajax enabled WCF Service' or WebHttpBinding with enableClientScript counts as REST but they do not recognize ISO dates, still have to use the /Date()/ munge.

JSON.stringifyWcf = function(json) {
    /// <summary>
    /// Wcf specific stringify that encodes dates in the
    /// a WCF compatible format ("/Date(9991231231)/")
    /// Note: this format works ONLY with WCF. 
    ///       ASMX can use ISO dates as of .NET 3.5 SP1
    /// </summary>
    /// <param name="key" type="var">property name</param>
    /// <param name="value" type="var">value of the property</param>
 
    // choking on FF 3.6 date Wed Feb 24 2010 14:02:17 GMT-0700 (US Mountain Standard Time)
    return JSON.stringify(json, function(key, value) {
        if (typeof value == "string") {
            var a = reISO.exec(value);
            if (a) {
                // SKY: the '-0000' forces the serverside serializer into utc mode resulting in accurate dates.
                var val = '/Date(' + new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])).getTime() + '-0000)/';
                
                //SKY: rick, I don't think that asking for a JSON representation
                // of an object should modify the state of the object. my 2 pesos.
                
                //this[key] = val;
                
                // this is the value that SHOULD be getting serialized but in the
                // FF native JSON is ignoring this and serializing the member so I am guessing
                // that is why rick is modifying the input object. gets the serialization job
                // done properly but is a rather nasty undocumented side effect, in my opinion.
                // I think it is probably better to overwrite native JSON with json2.js and
                // get consistant results across platforms with out the need to modify the input
                // UNLESS of course you are never going to need to use any of the objects that you
                // serialize... 
                return val; 
            }
        }
        return value;
    })
};

sky sanders
February 24, 2010

# re: Native JSON Parsing: What does it mean?

To see the tests that prove the bug in FF JSON replacer implementation see http://skysanders.net/Tests/NativeJSONTests/JSON.tests.htm

I have run it on browserlab and confirm that IE, safari and chrome native support is to spec.

techno
September 17, 2010

# re: Native JSON Parsing: What does it mean?

Hi Rick,
I wanted to upload a file by asynchronous call to a web service method. How will the JSON call look like in this case ?

thanks!!!

Burbujas en .NET
November 23, 2010

# Trick: Enviar datos en JSON usando POST

Muy buenas! Una de las preguntas que mucha gente se formula cuando empieza a hacer cosillas con ajax

Sushil
April 14, 2011

# re: Native JSON Parsing: What does it mean?

I want to thank you for such a nice post. I have been reading your posts for quite some time and love to read'em again n again. It really helps to understand the technology in depth.

Thanks

Beowshawitz
March 09, 2012

# re: Native JSON Parsing: What does it mean?

Been using this for a while now, and I just came across this issue. Our form has a DOB field, where a user picks the date from the jQuery date picker. The date is saved correctly in the database, but when it is being displayed in the form, there is a problem.

I've tracked it down to the JSON2.dateStringToDate function. His birthday is 4/22/1959, which comes across as /Date(-337550400000-0400)/

The function splits on the '-' character so this is lost. When the function completes, I am left with a DOB of 12/31/1969.

I altered the file right after the var b...
I added a line if(b[0] == "") { return new Date(-b[1]); } and my dates were working ok.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024