JavaScript Object Notation (JSON) has become the de facto data transfer standard for client-side Web applications that use JavaScript. JSON is a JavaScript-based object/value encoding format that looks very close to raw JavaScript and can be very easily parsed by JavaScript code because JavaScript can effectively evaluate a JSON string and re-materialize an object from it. Unlike XML there’s no parsing involved in the process, so it’s easy to work with and also relatively quick because the actual parsing (or rather evaluating) of a JSON string is done internally in the JavaScript engine rather than through manual code. The format and data types are also well defined so it’s easy to generate JSON strings in other languages like .NET (although parsing is a bit more complex).
JSON has been part of the EcmaScript specification for some time, but it really got its day in the sun because of AJAX. AJAX has opened up client applications to retrieve additional data from the server once a page has been loaded and JSON has become a key component to encode that data that travels over the network. Although JSON is fairly straightforward to create and parse, it still requires some sort of library to do so.
If you’ve been building AJAX applications, I’m sure JSON is not news to you and JSON support has found its way into most client libraries and server libraries for .NET as well-it’s become a core requirement for Web applications. This is nothing new as JSON usage should be familiar to most developers who are building AJAX applications. However, what is new is that the latest crop of browsers that have just been or are shortly going to be released ship with native, built-in JSON support, so JSON support libraries won’t be required.
JSON serialization and deserialization is now part of the EcmaScript 3.1 standard and so is likely to become ubiquitous in all browsers going forward. Internet Explorer 8 has shipped and 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.5 Beta 4
Native JSON Support, Finally
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, upon which native JSON support is based. FireFox 3.5 will also have a 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. Besides the fact that .stringify() is a horrible name for a method, JSON2 and its pre-runner, the original JSON classes, have been in use for a long time by many AJAX developers.
Both JSON2 and the native parsers are implemented as objects on the window object: window.JSON or simply the global JSON variable gives you access to the parser.
It’s good news that the native JSON parsers follow the JSON2 interface-if you are already using JSON2 in your apps, the native parsers will kick in without any code changes-you simply get the improved performance for free. The JSON2 script 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 library or the native JSON object you can write code like that in Listing 1.
Listing1: Simple JSON encoding and decoding
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
};
if (!JSON) {
alert("JSON parser missing.");
return;
}
var json = JSON.stringify(inst);
alert(json);
var inst2 = JSON.parse(json);
assert(inst2.name == inst.name &&
inst2.accesses == inst.accesses);
This works well to serialize and deserialize JSON with essentially a few lines of code. But there’s a small catch.
Those Pesky Dates-Still!
One prickly issue with JSON parsing in general is parsing and serialization of dates. JavaScript doesn’t have a date literal, which means there’s no 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. That consistency is a good thing and covers the serialization part of the process.
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 as shown above rather than data object, and some additional conversion is required to turn the date string into a date object.
In order to fix up the date value you can use a reviver function as the second parameter on .parse(). It is easy enough to do with some RegEx code, but it’s not exactly intuitive and definitely something that you’d want to stick into a library for reusability.
I like to extend the JSON object itself with a couple of methods that provide the date parsing so that it works either against the native or the JSON2-based JSON object (see Listing 2):
Listing 2: Parsing dates from ISO and MS AJAX formats
var reISO = /^(\d{4})-(\d{2})- // one line
(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;
var reMsAjax = /^\/Date\((d|-|.*)\)\/$/;
JSON.parseWithDate = function(json) {
var res = 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;
});
return res;
};
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 entire object structure after the JSON parsing is complete and gives the function a chance to “fix up” any of the values and child values in the object graph. In this case, you need to look for a string and match the regEx expression for the date format. This version of .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 is 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.
Since there is a fairly big performance hit for the reviver, I also added a .dateStringToDate() function, which can be used after you’ve called .parse() only. This gives you more control over when the conversion actually occurs and can be more efficient because you only apply the checks and actual conversions against fields that you know are dates. 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",
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 script code in Listing 3 to compare performance of native vs. json2.js behavior:
Listing 3: Comparing JSON performance in various browsers
<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})- // one line
(\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>
You can easily test and compare native vs. json2 JSON parsers by using IE 8; Standards mode supports the native JSON object and compatibility view doesn’t. You can click on the broken Web page icon next to the address bar to toggle between standards and compatibility mode. To test FireFox you’ll need to have side-by-side versions of FireFox 3.0 and 3.5 installed.
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 native stringify in particular is over 10 times faster than the json2 code (in compatibility mode). FireFox is more moderate with 2-3 times improvement for stringify, although it’s much faster in json2 parsing than IE is. Parsing is about 2-3 times faster both in IE and FireFox, both for native parsing. Using a reviver brings the improvements to about a 3-4x performance improvement as the object walking is internalized in the native parsers and so much more efficient than the corresponding JavaScript code.
In short, there’s a big performance gain to be had by utilizing the native JSON objects and the best part is that because it matches the JSON2 interface exactly, no changes should be required if you were already using JSON2.
Of course, this is only going to make a difference on sizable sets of data to be parsed and serialized. Personally I often retrieve fairly large chunks of data from the server to be used on the client and the improved JSON parsing performance can make a big difference in UI performance. While AJAX downloads happen asynchronously in the background, JSON parsing happens on the UI thread and so everything is on hold while JSON parses. Any improvements in this parsing and encoding translates into a more responsive UI experience.
Although native JSON support is coming to the major browsers soon, we unfortunately can’t assume that it’s always in place. So we still need to keep libraries like json2.js around for those browsers that don’t support it. The fact that the native parsers are a drop in replacement and offer significantly better performance makes this a no-brainer to take advantage of.
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 includes the date format I already discussed. MS JSON encoders use 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-use the JSON.parseWithDate() and JSON.dateStringToDate() functions I showed earlier. Both functions support parsing of the MS AJAX date format as well as the ISO format.
Of course, the point is moot if you’re using the Microsoft AJAX Library since it handles the conversions automatically. However, if you’re not utilizing the Microsoft AJAX Library using jQuery or some other standalone JavaScript library, then the native JSON parsing will likely be of interest to you.
I suspect Microsoft will integrate the Microsoft AJAX Library with the native JSON parsers as well to partake in the performance gains in the next version of ASP.NET.
Win-Win
The new native JSON support in browsers is a welcome addition and a surprising one as the integration has happened rather quickly for the plodding world of browser standards. That’s most likely because browser vendors have quickly realized that JSON is a key component of rich client applications in the browser and native implementations provide the performance and security needed. It’s nice to see this development and I hope to see more positive feature improvements like this in browsers in the future.