Douglas Crockford
douglas@crockford.com
2010-11-18
JSON is a light-weight, language independent, data interchange format. See: http://www.JSON.org/
The files in this collection implement JSON encoders/decoders in JavaScript.
JSON became a built-in feature of JavaScript when the ECMAScript Programming
Language Standard - Fifth Edition was adopted by the ECMA General Assembly
in December 2009. Most of the files in this collection are for applications
that are expected to run in obsolete web browsers. For most purposes, json2.js
is the best choice.
This file creates a JSON property in the global object, if there
isn't already one, setting its value to an object containing a stringify
method and a parse
method. The parse
method uses the eval
method to do the
parsing, guarding it with several regular expressions to defend against
accidental code execution hazards. On current browsers, this file does nothing,
prefering the built-in JSON object.
This file does everything that json2.js
does. It also adds a
toJSONString
method and a parseJSON
method to Object.prototype. Use of this
file is not recommended.
This file contains an alternative JSON parse
function that
uses recursive descent instead of eval
.
This files contains an alternative JSON parse
function that
uses a state machine instead of eval
.
This file contains two functions, JSON.decycle
and JSON.retrocycle
,
which make it possible to encode cyclical structures and dags in JSON, and to
then recover them. JSONPath is used to represent the links.
http://GOESSNER.net/articles/JsonPath/
Effectively this allows to create a deep copy of an object or to stringify it. Even with some cyclical objects.
Decycle handles duplicate references. So for example you can decycle an array containg a reference to itself:
var a = [];
a[0] = 'something'
a[1] = a;
console.log(JSON.stringify(
JSON.decycle(a)
));
// -> `["something",{"$ref":"$"}]`
So what decycle did was replacing a
with a special object ({"$ref":"$"}
). That object is just a plain object, but it is something JSON.retrocycle
would understand.
The duplicate references (which might be forming cycles) are replaced with an object of the form:
{$ref: PATH}
Where the PATH is a JSONPath string that locates the first occurance.
JSONPath is used to locate the unique object. $
indicates the top level of the object or array. [NUMBER]
or [STRING]
indicates a child member or property.
Lets create a simple helper class:
Cycles = class {
stringify(obj) {
return JSON.stringify(
JSON.decycle(a)
);
}
parse(str) {
return JSON.retrocycle(
JSON.parse(str)
);
}
clone(obj) {
return this.parse(
this.stringify(obj)
);
}
}
cycles = new Cycles();
So now we can easily do cloning. For example:
var a = [];
a[0] = 'something'
a[1] = a;
console.log(cycles.stringify(a));
b = cycles.clone(a);
// at this point a and be will be identical
console.log(a, b);
// but b is not the same object
b.push('test');
console.log(a, b);
You will notice that b.lenght is 3, but a.length is still 2. Also note that b[1] will be the same array (b[1] = b
).
This fork contains extra parameter for JSON.decycle
(JSON.decycle = function (object, stringifyNodes)
). When stringifyNodes
is true then DOM nodes are more identifiable. This is great for debugging.
Use JSON.stringify(JSON.decycle(variable, true))
if you expect that variable
can contain DOM Nodes.
Here is how the two versions of decycle compare:
var a = [];
a[0] = 'something'
a[1] = document.getElementById('some-element');
console.log(JSON.stringify(
JSON.decycle(a)
));
// -> ["something",{}]
console.log(JSON.stringify(
JSON.decycle(a, true)
));
// -> ["something","div#some-element"]
So nodes are kind of replaced with their selectors. In a way. Here are some examples:
- id:
<div id="some-element">
->"div#some-element"
- classes:
<div class="some-class hidden">
->"div.some-class.hidden{textContent:Lorem ipsum dolor si...}"
(text content is shortened to 20 chars) - other elements:
<tab-component>
->"tab-component{textContent:Lorem ipsum dolor si...}"
Note! The stringifyNodes
option is good for debugging, but retrocycle will not create DOM elements. For that you would have to create your own serializer and deserializer.