public
Last active

Tobie Langel: Prototype's JSON API

  • Download Gist
gistfile1.md
Markdown

Here's a (not so brief) explanation on the recent changes and the upcoming plans for our JSON API.

Our previous JSON implementation was based on Douglas Crockford's original proposal in which primitives, Array and Object prototypes each had their own toJSONString method. We mimicked that behaviour with two exceptions: 1) we did not extend Object.prototype and 2) we called our methods toJSON instead of toJSONString because our API was slightly different (Crockford's implementation took an extra arguments for prettifying the output string; ours didn't), and because we wanted to avoid naming collisions.

Unfortunately, the specification was modified a number of times until it was stabilized in ES5's final draft not long ago.

First of all, the toJSONString method was dropped in favor of a toJSON method (hello naming collision).

Secondly, all but Date.prototype.toJSON methods where dropped.

Finally, where the toJSONString method returned...a string, the new toJSON method now returned a native object or primitive ready to be stringified:

new Date().toJSONSting(); 
// -> "\"2010-02-26T16:23:40Z\"" 
new Date().toJSON(); 
// -> "2010-02-26T16:23:40Z" 

Notice how the first example returns a quoted string while the return value of the second one isn't quoted.

Our JSON implementation had the method names of the ES5 implementations with the behaviour of the original JSON spec. That obviously caused a lot of issues with services which relied on the native JSON object when present, hence the decision to modify this behaviour for our next release (1.7).

String.prototype.evalJSON behaves like JSON.parse except:

a) it does not accept a reviver, b) it internally calls String.prototype.unfilterJSON to help protect against JavaScript Hijacking.

Whenever the native JSON object is present and works correctly String.prototype.evalJSON acts as a wrapper around it. Whenever the native JSON object isn't present (or is broken), we provide our own implementation. In which case the JSON-formatted string is not parsed but evaled, but can be sanitized using JSON2's regexp tests by setting the sanitize flag to true.

Object.toJSON behaves like JSON.stringify except:

a) it doesn't accept replacer and space arguments, and b) Object.toJSON(Object) will yield undefined instead of {} (not that this seems like such a big deal).

Again, whenever the native JSON object is present and works properly our implementation just acts like a wrapper around it.

The reason for not providing the JSON object when it's missing is threefold:

  1. For now, we don't judge the reviver, replacer and prettifier options necessary. Providing our own API allows us to avoid implementing those for the time being.

  2. Some browsers' JSON implementations are broken. If we weren't relying on a wrapper, we'd be forced to replace these altogether.

  3. The native API is made of static methods, so being forced to use a wrapper API has a lot less impact on code readability than when "instance" methods are missing.

Compare:

JSON.stringify(foo);
// and
namespace.JSON.stringify(foo);

with

[1, 2, 3].map(function(e) { return i++; }).join(', ');
// and
namespace.map([1, 2, 3], function(e) { return i++; }).join(', ');

I can imagine that our JSON implementation in Prototype 2.0 will match the native API more closely but will stay in it's own namespace (as in the example above).

Hope this clarifies the recent changes.

Best,

Tobie

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.