Chapter 20

JSON

WHAT’S IN THIS CHAPTER?

  • Understanding JSON syntax
  • JSON parsing
  • JSON serialization

There was a time when XML was the de facto standard for transmitting structured data over the Internet. The first iteration of web services was largely XML-based, highlighting its target of server-to-server communication. XML was not, however, without its detractors. Some believed that the language was overly verbose and redundant. Several solutions arose to counter these problems, but the Web had already started moving in a new direction.

Douglas Crockford first specified JavaScript Object Notation (JSON) as IETF RFC 4627 in 2006 even though it was in use as early as 2001. JSON is a strict subset of JavaScript, making use of several patterns found in JavaScript to represent structured data. Crockford put forth JSON as a better alternative to XML for accessing structured data in JavaScript, since it could be passed directly to eval() and didn’t require the creation of a DOM.

The most important thing to understand about JSON is that it is a data format, not a programming language. JSON is not a part of JavaScript even though they share syntax. JSON is also not solely used by JavaScript, since it is a data format. There are parsers and serializers available in many programming languages.

SYNTAX

JSON syntax allows the representation of three types of values:

  • Simple Values — Strings, numbers, Booleans, and null can all be represented in JSON using the same syntax as JavaScript. The special value undefined is not supported.
  • Objects — The first complex data type, objects represent ordered key-value pairs. Each value may be a primitive type or a complex type.
  • Arrays — The second complex data type, arrays represent an ordered list of values that are accessible via a numeric index. The values may be of any type, including simple values, objects, and even other arrays.

There are no variables, functions, or object instances in JSON. JSON is all about representing structured data, and although it shares syntax with JavaScript, it should not be confused with JavaScript paradigms.

Simple Values

In its simplest form, JSON represents a small number of simple values. For example, the following is valid JSON:

5

This is JSON that represents the number 5. Likewise, the following is also valid JSON representing a string:

"Hello world!"

The big difference between JavaScript strings and JSON strings is that JSON strings must use double quotes to be valid (single quotes causes a syntax error).

Boolean values and null are valid exactly as they are as stand-alone JSON. In practice, however, JSON is most often used to represent more complex data structures of which simple values represent just part of the overall information.

Objects

Objects are represented using a slight modification of object literal notation. Object literals in JavaScript look like this:

var person = {
    name: "Nicholas",
    age: 29
};

While this is the standard way that developers create object literals, it’s the quoted property format that is used in JSON. The following is exactly the same as the previous example:

var object = {
    "name": "Nicholas",
    "age": 29
};

The JSON representation of this same object is then:

{
    "name": "Nicholas",
    "age": 29
}

There are a couple of differences from the JavaScript example. First, there is no variable declaration (variables don’t exist in JSON). Second, there is no trailing semicolon (not needed since this isn’t a JavaScript statement). Once again, the quotes around the property name are required to be valid JSON. The value can be any simple or complex value, allowing you to embed objects within objects, such as:

{
    "name": "Nicholas",
    "age": 29,
    "school": {
        "name": "Merrimack College",
        "location": "North Andover, MA"
    }
}

This example embeds school information into the top-level object. Even though there are two properties called "name", they are in two different objects and so are allowed. You do want to avoid having two properties of the same name in the same object.

Unlike JavaScript, object property names in JSON must always be double-quoted. It’s a common mistake to hand-code JSON without these double quotes or using single quotes.

Arrays

The second complex type in JSON is the array. Arrays are represented in JSON using array literal notation from JavaScript. For example, this is an array in JavaScript:

var values = [25, "hi", true];

You can represent this same array in JSON using a similar syntax:

[25, "hi", true]

Note once again the absence of a variable or a semicolon. Arrays and objects can be used together to represent more complex collections of data, such as:

[
    {
        "title": "Professional JavaScript",
        "authors": [
            "Nicholas C. Zakas"
        ],
        edition: 3,
        year: 2011
    },
    {
        "title": "Professional JavaScript",
        "authors": [
            "Nicholas C. Zakas"
        ],
        edition: 2,
        year: 2009
    },
    {
        "title": "Professional Ajax",
        "authors": [
            "Nicholas C. Zakas",
            "Jeremy McPeak",
            "Joe Fawcett"
        ],
        edition: 2,
        year: 2008
    },
    {
        "title": "Professional Ajax",
        "authors": [
            "Nicholas C. Zakas",
            "Jeremy McPeak",
            "Joe Fawcett"
        ],
        edition: 1,
        year: 2007
    },
    {
        "title": "Professional JavaScript",
        "authors": [
            "Nicholas C. Zakas"
        ],
        edition: 1,
        year: 2006
    }
]

This array contains a number of objects representing books. Each object has several keys, one of which is "authors", which is another array. Objects and arrays are typically top-level parts of a JSON data structure (even though this is not required) and can be used to create a large number of data structures.

PARSING AND SERIALIZATION

JSON’s rise to popularity was not necessarily because it used familiar syntax. More so, it was because the data could be parsed into a usable object in JavaScript. This stood in stark contrast to XML that was parsed into a DOM document, making extraction of data into a bit of a chore for JavaScript developers. For example, the JSON code in the previous section contains a list of books, and you can easily get the title of the third book via:

books[2].title

This assumes that the data structure was stored in a variable named books. Compare this to a typical walk through a DOM structure:

doc.getElementsByTagName("book")[2].getAttribute("title")

With all of the extra method calls, it’s no wonder that JSON became incredibly popular with JavaScript developers. After that, JSON went on to become the de facto standard for web services.

The JSON Object

Early JSON parsers did little more than use JavaScript’s eval() function. Since JSON is a subset of JavaScript’s syntax, eval() could parse, interpret, and return the data as JavaScript objects and arrays. ECMAScript 5 formalized JSON parsing under a native global called JSON. This object is supported in Internet Explorer 8+, Firefox 3.5+, Safari 4+, Chrome, and Opera 10.5+. A shim for older browsers can be found at https://github.com/douglascrockford/JSON-js. It’s important not to use eval() alone for evaluating JSON in older browsers because of the risk of executable code. The JSON shim is the best option for browsers without native JSON parsing.

The JSON object has two methods: stringify() and parse(). In simple usage, these methods serialize JavaScript objects into a JSON string and parse JSON into a native JavaScript value, respectively. For example:

image
var book = {
               title: "Professional JavaScript",
                authors: [
                    "Nicholas C. Zakas"
                ],
                edition: 3,
                year: 2011
           };
 
var jsonText = JSON.stringify(book);

JSONStringifyExample01.htm

This example serializes a JavaScript object into a JSON string using JSON.stringify() and stores it in jsonText. By default, JSON.stringify() outputs a JSON string without any extra white space or indentation, so the value stored in jsonText is:

{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,
"year":2011}

When serializing a JavaScript object, all functions and prototype members are intentionally omitted from the result. Additionally, any property whose value is undefined is also skipped. You’re left with just a representation of the instance properties that are one of the JSON data types.

A JSON string can be passed directly into JSON.parse() and it creates an appropriate JavaScript value. For example, you can create an object similar to the book object using this code:

var bookCopy = JSON.parse(jsonText);

Note that book and bookCopy are each separate objects without any relationship to one another even though they do share the same properties.

An error is thrown if the text passed into JSON.parse() is not valid JSON.

Serialization Options

The JSON.stringify() method actually accepts two arguments in addition to the object to serialize. These arguments allow you to specify alternate ways to serialize a JavaScript object. The first argument is a filter, which can be either an array or a function, and the second argument is an option for indenting the resulting JSON string. When used separately or together, this provides some very useful functionality for controlling JSON serialization.

Filtering Results

If the argument is an array, then JSON.stringify()will include only object properties that are listed in the array. Consider the following:

image
var book = {
               "title": "Professional JavaScript",
                "authors": [
                    "Nicholas C. Zakas"
                ],
                edition: 3,
                year: 2011
           };
 
var jsonText = JSON.stringify(book, ["title", "edition"]);

JSONStringifyExample01.htm

The second argument to JSON.stringify() is an array with two strings: "title" and "edition". These correspond to properties in the object being serialized, and so only those properties appear in the resulting JSON string:

{"title":"Professional JavaScript","edition":3}

When the second argument is a function, the behavior is slightly different. The provided function receives two arguments: the property key name and the property value. You can look at the key to determine what to do with the property. The key is always a string but might be an empty string if a value isn’t part of a key-value pair.

In order to change the serialization of the object, return the value that should be included for that key. Keep in mind that returning undefined will result in the property being omitted from the result. Here’s an example:

image
var book = {
               "title": "Professional JavaScript",
                "authors": [
                    "Nicholas C. Zakas"
                ],
                edition: 3,
                year: 2011
           };
 
var jsonText = JSON.stringify(book, function(key, value){
    switch(key){
        case "authors":
            return value.join(",")
         
        case "year":
            return 5000;
            
        case "edition":
            return undefined;
            
        default:
            return value;
    }
});

JSONStringifyExample02.htm

The function filters based on the key. The "authors" key is translated from an array to a string, the "year" key is set to 5000, and the "edition" key is removed altogether by returning undefined. It’s important to provide a default behavior that returns the passed-in value so that all other values are passed through to the result. The first call to this function actually has key equal to an empty string and the value set to the book object. The resulting JSON string is:

{"title":"Professional JavaScript","authors":"Nicholas C. Zakas","year":5000}

Keep in mind that filters apply to all objects contained in the object to be serialized, so an array of multiple objects with these properties will result in every object including only the "title" and "edition" properties.

Firefox 3.5–3.6 had a bug in its implementation of JSON.stringify() when a function was used as the second argument. The function can act only as a filter: returning undefined means the property is skipped, while returning anything else causes the property to be included. This behavior was fixed in Firefox 4.

String Indentation

The third argument of JSON.stringify() controls indentation and white space. When this argument is a number, it represents the number of spaces to indent each level. For example, to indent each level by four spaces, use the following:

image
var book = {
               "title": "Professional JavaScript",
                "authors": [
                    "Nicholas C. Zakas"
                ],
                edition: 3,
                year: 2011
           };
 
var jsonText = JSON.stringify(book, null, 4);

JSONStringifyExample03.htm

The string stored in jsonText is:

{
    "title": "Professional JavaScript",
    "authors": [
        "Nicholas C. Zakas"
    ],
    "edition": 3,
    "year": 2011
}

You may have noticed that JSON.stringify() also inserts new lines into the JSON string for easier reading. This happens for all valid indentation argument values. (Indentation without new lines isn’t very useful.) The maximum numeric indentation value is 10; passing in a value larger than 10 automatically sets the value to 10.

If the indentation argument is a string instead of a number, then the string is used as the indentation character for the JSON string instead of a space. Using a string, you can set the indentation character to be a tab or something completely arbitrary like two dashes:

var jsonText = JSON.stringify(book, null, " – -");

The jsonText value then becomes:

{
--"title": "Professional JavaScript",
--"authors": [
----"Nicholas C. Zakas"
--],
--"edition": 3,
--"year": 2011
}

There is a ten-character limit on the indentation string to use. If a string longer than ten characters is used, then it is truncated to the first ten characters.

The toJSON() Method

Sometimes objects need custom JSON serialization above and beyond what JSON.stringify() can do. In those cases, you can add a toJSON() method to the object and have it return the proper JSON representation for itself. In fact, the native Date object has a toJSON() method that automatically converts JavaScript Date objects into an ISO 8601 date string (essentially, the same as calling toISOString() on the Date object).

A toJSON() method can be added to any object, for example:

image
var book = {
               "title": "Professional JavaScript",
                "authors": [
                    "Nicholas C. Zakas"
                ],
                edition: 3,
                year: 2011,
                toJSON: function(){
                    return this.title;
                }
           };
 
var jsonText = JSON.stringify(book);

JSONStringifyExample05.htm

This code defines a toJSON() method on the book object that simply returns the title of the book. Similar to the Date object, this object is serialized to a simple string instead of an object. You can return any serialization value from toJSON(), and it will work appropriately. Returning undefined causes the value to become null if the object is embedded in another object or else is just undefined if the object is top-level.

The toJSON() method can be used in addition to the filter function, and so it’s important to understand the order in which the various parts of a serialization process take place. When an object is passed into JSON.stringify(), the following steps are taken:

1. Call the toJSON() method if it’s available to retrieve the actual value. Use the default serialization otherwise.

2. If the second argument is provided, apply the filter. The value that is passed into a filter function will be the value returned from step 1.

3. Each value from step 2 is serialized appropriately.

4. If the third argument is provided, format appropriately.

It’s important to understand this order when deciding whether to create a toJSON() method or to use a filter function or to do both.

Parsing Options

The JSON.parse() method also accepts an additional argument, which is a function that is called on each key-value pair. The function is called a reviver function to distinguish it from the replacer (filter) function that JSON.stringify() accepts, even though the format is exactly the same: the function receives two arguments, the key and the value, and needs to return a value.

If the reviver function returns undefined, then the key is removed from the result; if it returns any other value, that value is inserted into the result. A very common use of the reviver function is to turn date strings into Date objects. For example:

image
var book = {
               "title": "Professional JavaScript",
                "authors": [
                    "Nicholas C. Zakas"
                ],
                edition: 3,
                year: 2011,
                releaseDate: new Date(2011, 11, 1)
           };
 
var jsonText = JSON.stringify(book);
 
var bookCopy = JSON.parse(jsonText, function(key, value){
    if (key == "releaseDate"){
        return new Date(value);
    } else {
        return value;
    }
});
 
alert(bookCopy.releaseDate.getFullYear());

JSONParseExample02.htm

This code starts with the addition of a releaseDate property to the book object, which is a Date. The object is serialized to get a valid JSON string and then parsed back into an object, bookCopy. The reviver function looks for the "releaseDate" key and, when found, creates a new Date object based on that string. The resulting bookCopy.releaseDate property is then a Date object so the getFullYear() method can be called.

SUMMARY

JSON is a lightweight data format designed to easily represent complex data structures. The format uses a subset of JavaScript syntax to represent objects, arrays, strings, numbers, Booleans, and null. Even though XML can handle the same job, JSON is less verbose and has better support in JavaScript.

ECMAScript 5 defines a native JSON object that is used for serialization of objects into JSON format and for parsing JSON data into JavaScript objects. The JSON.stringify() and JSON.parse() methods are used for these two operations, respectively. Both methods have a number of options that allow you to change the default behavior to filter or otherwise modify the process.

The native JSON object is well supported across browsers, including Internet Explorer 8+, Firefox 3.5+, Safari 4+, Opera 10.5, and Chrome.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.220.22