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:
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
Other Posts you might also like