JavaScript’s JSON API
Join the DZone community and get the full member experience.
Join For FreeJSON is a plain text data storage format. This blog post describes what it is and how to work with it via an ECMAScript 5 API.
1. Overview
JSON is an abbreviation of “JavaScript Object Notation”. JSON stores data as plain text. Its grammar is a subset of the grammar of JavaScript expressions. Example:{ "first": "Jane", "last": "Porter", "married": true, "born": 1890, "friends": [ "Tarzan", "Cheeta" ] }JSON uses the following constructs from JavaScript expressions:
- Compound: objects of JSON data, arrays of JSON data
- Atomic: strings, numbers, booleans, null
- Strings must always be double-quoted, string literals such as 'mystr' are illegal.
- Property names must be double-quoted.
- Douglas Crockford discovered JSON in 2001 [1]. He gave it a name and put up a specification at json.org. Quote:
I discovered JSON. I do not claim to have invented JSON, because it already existed in nature. What I did was I found it, I named it, I described how it was useful. I don't claim to be the first person to have discovered it; I know that there are other people who discovered it at least a year before I did. The earliest occurrence I've found was, there was someone at Netscape who was using JavaScript array literals for doing data communication as early as 1996, which was at least five years before I stumbled onto the idea.
- The JSON specification has been translated to many human languages and there are now libraries for many programming languages that support parsing and generating JSON.
- Initially, Crockford wanted JSON to have the name “JavaScript Markup Language” [1], but the acronym JSML was already taken by the “JSpeech Markup Language”.
2. Grammar
Minimalism has always been a core goal for JSON. Its grammar even fits on a business card:![]() |
The JSON grammar on a business card. [source] |
- object
- { }
- { members }
- members
- pair
- pair , members
- pair
- string : value
- array
- [ ]
- [ elements ]
- elements
- value
- value , elements
- value
- object
- array
- string
- number
- true
- false
- null
- string
- ""
- " chars "
- chars
- char
- char chars
- char
- <any Unicode character except " or \ or control characters>
- \\ \" \/ \b \f \n \r \t
- \u <four hex digits>
- number
- int
- int frac
- int exp
- int frac exp
- int
- digit
- digit1-9 digits
- - digit
- - digit1-9 digits
- frac
- . digits
- exp
- e digits
- digits
- digit
- digit digits
- e
- e e+ e-
- E E+ E-
3. The JSON API
This section describes several methods for creating and parsing JSON data. First, we have to understand how two of those methods let you hand in a function to customize their operation.3.1. Node visitors
The methods JSON.stringify() and JSON.parse() allow you to customize how they work by passing in a function. Let’s call that function a node visitor. Both methods iterate over a tree of JavaScript values whose compound nodes are arrays and objects and whose leaves are primitive values (booleans, numbers, strings). They call the visitor for each node. It then has the option to replace or delete it.- JSON.stringify iterates over a value before stringifying it.
- JSON.parse iterates over the result of parsing a JSON text.
function (key, value)The parameters are:
- this: the parent of the current node. The root value has a special parent, an object whose only property has the empty string as its name and the root as its value.
- key: a key where the current node is located inside its parent. key is always a string. It is the empty string if there is no parent.
- value: the current node.
function nodeVisitor(key, value) { console.log( JSON.stringify(this) // parent +"#"+JSON.stringify(key) +"#"+JSON.stringify(value)); return value; // don't change }The above function only uses JSON.stringify(), because it is a good way of displaying JavaScript values. It is used below to examine how iteration works.
Order and kind of nodes visited – stringify. The special root node comes first, in a prefix iteration (parent before children). The last values are the results of the function calls.
> JSON.stringify(["a","b"], nodeVisitor) {"":["a","b"]}#""#["a","b"] ["a","b"]#"0"#"a" ["a","b"]#"1"#"b" <span class="result">'["a","b"]'</span> > JSON.stringify({"a":1, "b":2}, nodeVisitor) {"":{"a":1,"b":2}}#""#{"a":1,"b":2} {"a":1,"b":2}#"a"#1 {"a":1,"b":2}#"b"#2 <span class="result">'{"a":1,"b":2}'</span> > JSON.stringify("abc", nodeVisitor) {"":"abc"}#""#"abc" <span class="result">'"abc"'</span>Order and kind of nodes visited – parse. The leaves come first, in a postfix iteration (children before parent). The last values are the results of the function calls.
> JSON.parse('["a","b"]', nodeVisitor) ["a","b"]#"0"#"a" ["a","b"]#"1"#"b" {"":["a","b"]}#""#["a","b"] [ 'a', 'b' ] > JSON.parse('{"a":1, "b":2}', nodeVisitor) {"a":1,"b":2}#"a"#1 {"a":1,"b":2}#"b"#2 {"":{"a":1,"b":2}}#""#{"a":1,"b":2} { a: 1, b: 2 } > JSON.parse('"hello"', nodeVisitor) {"":"hello"}#""#"hello" 'hello'
3.2. JSON.stringify(value, [replacer], [space])
translates the JavaScript value value to a text in JSON format. It has two optional arguments:- replacer: There are two ways this parameter can influence stringification:
- Node visitor (see above): replaces nodes in the tree of values. Example:
function replacer(key, value) { if (typeof value === "number") { value = 2 * value; } return value; }
Interaction:> JSON.stringify({ a: 5, b: [ 2, 8 ] }, replacer) '{"a":10,"b":[4,16]}'
- Whitelist of property names: hides all properties (of non-array objects) that are not in the list. Example:
JSON.stringify({foo: 1, bar: {foo: 1, bar: 1}}, ["bar"])
returns'{"bar":{"bar":1}}'
- Node visitor (see above): replaces nodes in the tree of values. Example:
- space: Without this parameter, the result of stringify is a single line of text.
> console.log(JSON.stringify({a: 0, b: ["\n"]})) {"a":0,"b":["\n"]}
With it, newlines are inserted and each level of nesting via arrays and objects increases indentation. There are two ways to specify how to indent:- Number: Multiply the number by the level of indentation
and indent the line by as many spaces. Numbers smaller that 0 are
interpreted as 0, numbers larger than 10 are interpreted as 10.
> console.log(JSON.stringify({a: 0, b: ["\n"]}, null, 2)) { "a": 0, "b": [ "\n" ] }
- String: To indent, repeat the given string once for each
level of indentation. Only the first 10 characters of the string are
used.
> console.log(JSON.stringify({a: 0, b: ["\n"]}, null, "|–")) { |–"a": 0, |–"b": [ |–|–"\n" |–] }
- Number: Multiply the number by the level of indentation
and indent the line by as many spaces. Numbers smaller that 0 are
interpreted as 0, numbers larger than 10 are interpreted as 10.
> JSON.stringify({ toJSON: function() { return "Cool" } }) '"Cool"'Dates already have a toJSON method which produces an ISO 8601 date string:
> JSON.stringify(new Date("2011-07-29")) '"2011-07-28T22:00:00.000Z"'The full signature of a toJSON method is as follows.
function(key)The key parameter allows you to stringify differently, depending on context. It is always a string and denotes where in the parent object your object was found:
- Root position: the empty string.
- Property value: the property name.
- Array elemen: the element index as a string.
var obj = { toJSON: function(key) { console.log(key) } } JSON.stringify({ foo: obj, bar: [ obj ]});Output:
foo 0
3.3. JSON.parse(text, [reviver])
parses the JSON data in text and returns a JavaScript value. Examples:> JSON.parse("'String'") // illegal single quotes SyntaxError: Unexpected token ILLEGAL > JSON.parse('"String"') 'String' > JSON.parse("123") 123 > JSON.parse("[1, 2, 3]") [ 1, 2, 3 ] > JSON.parse('{ "hello": 123, "world": 456 }') { hello: 123, world: 456 }The optional parameter reviver is a node visitor and can be used to transform the parsed data. Example: translating date strings to date objects.
function dateReviver(key, value) { if (typeof value === "string") { var x = Date.parse(value); if (!isNaN(x)) { // valid date string? return new Date(x); } } return value; }Note: You can also use a regular expression to check whether a given string is a date string.
> JSON.parse( '{ "name": "John", "birth": "2011-07-28T22:00:00.000Z" }', dateReviver) { name: 'John', birth: Thu, 28 Jul 2011 22:00:00 GMT }
4. Related reading
- Video: Douglas Crockford — The JSON Saga [2009-07-02]
Topics:
Opinions expressed by DZone contributors are their own.
Comments