Jump to Table of Contents

JSON

The JSON module is an implementation of the ECMA 5 specification for transforming data to and from JavaScript Object Notation. JSON is a safe, efficient, and reliable data interchange format. This module provides a JavaScript implementation of the spec, based on Douglas Crockford's json2.js. For browsers with native support it defers to the native implementation.

Getting Started

To include the source files for JSON and its dependencies, first load the YUI seed file if you haven't already loaded it.

<script src="http://yui.yahooapis.com/3.8.0/build/yui/yui-min.js"></script>

Next, create a new YUI instance for your application and populate it with the modules you need by specifying them as arguments to the YUI().use() method. YUI will automatically load any dependencies required by the modules you specify.

<script>
// Create a new YUI instance and populate it with the required modules.
YUI().use('json-parse', 'json-stringify', function (Y) {
    // JSON is available and ready for use. Add implementation
    // code here.
});
</script>

For more information on creating YUI instances and on the use() method, see the documentation for the YUI Global Object.

Using the JSON Utility

JSON module overview

The JSON module adds the namespace JSON to your YUI instance. Its methods are static, available from this namespace.

To minimize the code footprint when some functionality is not required, JSON has been broken up into the following modules:

json-parse
Adds Y.JSON.parse(..) method to the YUI instance. Use this module if all you will be doing is parsing JSON strings.
json-stringify
Adds Y.JSON.stringify(..) method and its supporting methods and properties to the YUI instance. Use this module if all you will be doing is serializing JavaScript objects to JSON strings.
json
A rollup of json-parse and json-stringify modules. Use this if you need to both parse JSON strings and serialize objects to JSON strings.

Parsing JSON strings into native JavaScript values

Provided a string containing data in JSON format, simply pass the string to parse and capture the return value.

YUI().use('json-parse', function (Y) {

var jsonString = '{"products":['+
    '{"id":1,"price":0.99,"inStock":true,"name":"grapes"},'+
    '{"id":2,"price":3.5,"inStock":false,"name":"passion fruit"},'+
    '{"id":3,"price":2.5,"inStock":true,"name":"bananas"}'+
']}';

// JSON.parse throws a SyntaxError when passed invalid JSON
try {
    var data = Y.JSON.parse(jsonString);
}
catch (e) {
    alert("Invalid product data");
}

// We can now interact with the data
for (var i = data.products.length - 1; i >= 0; --i) {
    var p = data.products[i];
    if (p.price < 1) {
        p.price += 1; // Price increase!
    }
}

});

Using the "reviver" parameter

The optional second parameter to parse accepts a function that will be executed on each member of the parsed JavaScript object. Each call to the reviver function is passed the key and associated value, and is executed from the context of the object containing the key. If the return value of the reviver is undefined, the key will be omitted from the returned object.

Typical uses of reviver functions are filtering, formatting, and value conversion.

YUI().use('json-parse', function (Y) {

    var jsonString = '{"products":['+
        '{"id":1,"price":0.99,"inStock":true,"name":"grapes"},'+
        '{"id":2,"price":3.5,"inStock":false,"name":"passion fruit"},'+
        '{"id":3,"price":2.5,"inStock":true,"name":"bananas"}'+
    ']}';

    // Remove all out of stock entries and bananas.  Format prices.
    var currencySymbol = '$';
    var reviver = function (key,val) {
        if (key === 'inStock' && !val) {
            return undefined;
        } else if (val === 'bananas') {
            return undefined;
        } else if (key === 'price') {
            val += val % 1 ? "0" : ".00";
            var pIdx = val.indexOf('.');
            val = pIdx ? "0" + val : val;
            val = currencySymbol + val.substr(0,pIdx + 3);
        }

        return val;
    };

    // JSON.parse throws a SyntaxError when passed invalid JSON
    try {
        var data = Y.JSON.parse(jsonString,reviver);
    }
    catch (e) {
        alert("Invalid product data");
    }

    // We can now interact with the data
    alert(data.products.length); // 1
    alert(data.products[0].price); // $0.99

});

A word of caution against using eval

JSON data format is a subset of JavaScript notation, meaning that it is possible to use JavaScript eval to transform JSON data to live data. However, it is unsafe practice to assume that data reaching your code is not malicious. eval is capable of executing JavaScript's full syntax, including calling functions and accessing cookies with all the privileges of a <script>. To ensure that the data is safe, the JSON module's parse method inspects the incoming string (using methods derived from Douglas Crockford's json2.js) and verifies that it is safe before giving it the green light to parse.

// UNSAFE
var data = eval('(' + shouldBeJsonData + ')');

// Safe
var data = Y.JSON.parse(shouldBeJsonData);

Creating JSON strings from JavaScript objects

To convert a JavaScript object (or any permissable value) to a JSON string, pass that object to Y.JSON.stringify and capture the returned string.

YUI().use("json-stringify", function (Y) {

    var myData = {
        troop : [
            { name: "Ashley", age: 12 },
            { name: "Abby", age:9 }
        ]
    }; 

    var jsonStr = Y.JSON.stringify(myData);

    alert(jsonStr); // {"troop":[{"name":"Ashley","age":12},{"name":"Abby","age":9}]}
});

Using a whitelist

To serialize only a fixed subset of keys, provide an array of key names as the second parameter to stringify.

YUI().use("json-stringify", function (Y) {

    // a detailed object record set
    var records = [
        {id:1, name: "Bob",   age: 47, favorite_color: "blue"},
        {id:2, name: "Sally", age: 30, favorite_color: "mauve"},
        {id:3, name: "Tommy", age: 13, favorite_color: "black"},
        {id:4, name: "Chaz",  age: 26, favorite_color: "plaid"}
    ];

    // Use an array of acceptable object key names as a whitelist.
    // To create a JSON string with only age and id
    var jsonStr = Y.JSON.stringify(records,["id","age"]);

    alert(jsonStr);
    // [{"id":1,"age":47},{"id":2,"age":30},{"id":3,"age":13},{"id":4,"age":26}]

});

Using a custom "replacer" function

For more granular control of how values in your object are serialized, you can pass a replacer function as the second parameter to stringify. The replacer will be called for each key value pair, and executed from the context of the key's host object. The return value of the replacer will be serialized in place of the raw value.

YUI().use("json-stringify", function (Y) {

    // a detailed object record set
    var records = [
        {id:1, name: "Bob",   birthdate: "2/27/1964", favorite_color: "blue"},
        {id:2, name: "Sally", birthdate: "9/30/1983", favorite_color: "mauve"},
        {id:3, name: "Tommy", birthdate: "3/11/1990", favorite_color: "black"},
        {id:4, name: "Chaz",  birthdate: "5/22/1975", favorite_color: "plaid"}
    ];

    // Create a replacer to blacklist the id and convert the birthdate to a Date
    var replacer = function (key,val) {
        // blacklist id and favorite_color
        if (key === 'id' || key === 'favorite_color') {
            return undefined;

        // convert birthdate to a Date instance (serialized as UTC date string)
        } else if (key === 'birthdate') {
            var d = new Date(),
                bd = val.split('/');
            d.setFullYear(bd[2],bd[0]-1,bd[1]);
            d.setHours(0,0,0);
            return d;
        }

        return val;
    };

    var jsonStr = Y.JSON.stringify(records,replacer);

    alert(jsonStr);
    // [{"name":"Bobby","birthdate":"1964-02-28T08:00:00Z"},{"name":"Sally","birthdate":"1983-09-30T07:00:00Z"},{"name":"Tommy","birthdate":"1990-03-11T08:00:00Z"},{"name":"Chaz","birthdate":"1975-05-23T07:00:00Z"}]

});

Formatting JSON output

To create readable JSON, pass a string or number as the third parameter to stringify. Object and Array members will be separated with newlines and indented. If a string is supplied, that string will be used for the indentation. If a number is passed, that number of spaces will be used.

YUI().use('json-stringify', function (Y) {

    var fam = {
        family: "Franklin",
        children: [ "Bob", "Emily", "Sam" ]
    };

    // format serialization with four spaces
    var jsonStr = Y.JSON.stringify(fam,null,4);

    alert(jsonStr);
    /*
    {
        "family": "Franklin",
        "children": [
            "Bob",
            "Emily",
            "Sam"
        ]
    }
    */

});

Catching JSON errors

ECMA 5 states that parse should throw an error when an invalid JSON string is input. It also states that stringify should throw an error when an object with cyclical references is input.

For this reason, it is recommended that both parse and stringify be called from within try/catch blocks.

try {
    // BOOM
    Y.JSON.parse("{'this string': is, not_valid: ['J','S','O','N'] }");
}
catch (e) {
    alert("A string may eval to the same object, but might not be valid JSON");
}

// This is safe to stringify
var myData = {
    troop : [
        { name: "Ashley", age: 12 },
        { name: "Abby", age:9 }
    ]
}; 

// Create a cyclical reference
myData.troop[0]["NEWKEY"] = myData;

try {
    // BOOM
    jsonStr = Y.JSON.stringify(myData);
} catch (e) {
    alert("Cyclical references can sneak in.  Remember to wrap stringify.");
}

Notes about current native JSON support

Native JSON support in JavaScript is defined in the ECMAScript 5 specification, which was finalized in September 2009. However, most of the major browser vendors had already implemented this feature in recent versions of their JavaScript engines while the spec was still in draft. As a result of the timing and the fact that native JSON is a new feature, there are gotchas involved in leveraging the disparate native behaviors.

In general, it is preferable to use the native behavior for JSON.parse as it is much faster and safer than any JavaScript implementation. There are also very few known critical issues with supporting browsers.

Stringify has more pitfalls and inconsistencies, but they may not affect your use cases. As with parse, the native implementation of stringify is faster, but only marginally so with reasonably sized input. In most cases, choosing the JavaScript implementation will not affect performance and may be preferable for its cross browser consistency.

As noted above, the JSON module will leverage native behavior in implementing browsers by default. However, you can choose to opt out of leveraging native parse or stringify by setting the Y.JSON.useNativeParse and Y.JSON.useNativeStringify static properties.

YUI().use('json', function (Y) {

    // Always use the JavaScript implementation for parsing
    Y.JSON.useNativeParse = false;

    // Always use the JavaScript implementation for stringifying
    Y.JSON.useNativeStringify = false;

    //...
});