Teach your XMLHttpRequest some JSON

I recently started to use JSON for the data exchange between websites and servers (Yeah, I know I’m late ;)) and was looking for a lightweight solution to send HTTP requests carrying JSON encoded data via JavaScript. The best idea I’ve found was the JSONRequest proposal on json.org, but this has two essential flaws: browser support is not really existent and it doesn’t allow RESTful communication.

So I stuck with the native XMLHttpRequest object and extended it’s functionality. The compressed code is just 822 Bytes and XMLHttpRequest compatible. You’re even able to send cross-domain-requests with a bit extra code (see my AJAX cross domain requests with CORS post for more). The code is reported to work with IE9, FF7, Chrome 16, Safari 5.1 and Opera 11.60 beta, but I’m pretty sure every browser with JSON and XMLHttpRequest support should work (tell me whether it works with your browser or not).

You might want to visit my little example page to test the whole thing right away, but I suggest to read a least the section about how to use the JSONHttpRequest object, before you download the code to use it in your page.

How to use the JSONHttpRequest

The JSONHttpRequest is a transparent extension of the standard XMLHttpRequest object. This means you can use all of its methods and properties defined in the W3C specification as well as any browser-specific additions. The difference between a normal XMLHttpRequest and a JSONHttpRequest are just two new properties and one new method:

  • sendJSON(data): converts any data to JSON and sends them via send(data)
  • responseJSON: responseText interpreted as JSON or null
  • strictJSON: whether JSON errors should throw an exception (default: true)

Calling sendJSON() will set the content-type to application/json;charset=encoding, if you haven’t set another value via setRequestHeader('Content-Type', value). encoding will be UTF8 in most cases. Everything else should be pretty straight forward, but let me know if you need more details.

Understanding the code

A look on the overall size and the number of additions implies that the code couldn’t be too complicated, but the XMLHttpRequest object seems to be a bit different that normal objects in some browsers. Especially prototyping won’t work, therefore the following code

JSONHttpRequest = new Function();
JSONHttpRequest.prototype = new XMLHttpRequest();
request = new JSONHttpRequest();
request.open('POST','page.html');

results for the IE in an invalid calling object, for Opera in a WRONG_THIS_ERR and for Safari in a TypeError exception. I fiddled around with __proto__ and getPrototypeOf(), but even that didn’t solve all issues, so I finally went another way.

In the end I used the JSONHttpRequest as a container for a XMLHttpRequest object, which is only visible within the closure (it’s more or less a private property). All methods of the XMLHttpRequest are accessible via some – dunno a better name – proxy methods:

this.open = function() {
  return _xmlHttpRequest.open.apply(_xmlHttpRequest, Array.prototype.slice.apply(arguments));
}

Sebastiano Armeli explained this whole apply-slice-arguments thing quite nicely in his blog, so I won’t do double work here ;) Instead I’ll continue with the properties, which are accessible in a similar way like the methods using getters and setters:

Object.defineProperty(this, 'onreadystatechange', {
  get: function() { return _xmlHttpRequest.onreadystatechange; },
  set: function(value) { _xmlHttpRequest.onreadystatechange = value; },
  enumerable: true,
  configurable: true
});

The actual implementation uses a for-loop, which runs over all enumerable properties of XMLHttpRequest, to set up the whole proxy stuff. This is done mainly to shrink the size of the code, but the idea is the same as sketched here. I think the rest of the code shouldn’t be too cryptic (even though I chose compressor friendly code over readability), but feel free to ask if anything is still unclear.

The famous last paragraph

Here are the example and download links once again. I hope my work is of some use for someone. Everything is open-source so don’t hesitate to improve anything and please tell me if you know a better solution to this problem. Happy coding :)