# Yahoo! UI Library

### json 2.4.1

Yahoo! UI Library > json > JSON.js (source view)

Show Private Show Protected

    /**
     * Provides methods to parse JSON strings and convert objects to JSON strings.
     * @module json
     * @requires yahoo
     * @class YAHOO.lang.JSON
     * @static
     */
    YAHOO.namespace('lang');
    YAHOO.lang.JSON = {

        /**
         * Parse a JSON string, returning the native JavaScript representation.
         * Only minor modifications from http://www.json.org/json.js.
         * @param s {string} JSON string data
         * @param filter {function} (optional) function(k,v) passed each key value pair of object literals, allowing pruning or altering values
         * @return {MIXED} the native JavaScript representation of the JSON string
         * @throws SyntaxError
         * @method parse
         * @static
         * @public
         */
        parse : function (s,filter) {
            var j;

            function walk(k, v) {
                var i, n;
                if (v && typeof v === 'object') {
                    for (i in v) {
                        if (YAHOO.lang.hasOwnProperty.apply(v, [i])) {
                            n = walk(i, v[i]);
                            if (n !== undefined) {
                                v[i] = n;
                            }
                        }
                    }
                }
                return filter(k, v);
            }

    // Parsing happens in three stages. In the first stage, we run the text against
    // a regular expression which looks for non-JSON characters. We are especially
    // concerned with '()' and 'new' because they can cause invocation, and '='
    // because it can cause mutation. But just to be safe, we will reject all
    // unexpected characters.

    // We split the first stage into 4 regexp operations in order to work around
    // crippling deficiencies in IE's and Safari's regexp engines. First we replace
    // all backslash pairs with '@' (a non-JSON character). Second, we replace all
    // simple value tokens with ']' characters. Third, we delete all open brackets
    // that follow a colon or comma or that begin the text. Finally, we look to see
    // that the remaining characters are only whitespace or ']' or ',' or ':' or '{'
    // or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/.test(s.replace(/\\./g, '@').
                    replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']').
                    replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

    // In the second stage we use the eval function to compile the text into a
    // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
    // in JavaScript: it can begin a block or an object literal. We wrap the text
    // in parens to eliminate the ambiguity.

                j = eval('(' + s + ')');

    // In the optional third stage, we recursively walk the new structure, passing
    // each name/value pair to a filter function for possible transformation.

                return typeof filter === 'function' ? walk('', j) : j;
            }

    // If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('parseJSON');
        },

        /**
         * Converts an arbitrary value to a JSON string representation.
         * Cyclical object or array references are replaced with null.
         * If a whitelist is provided, only matching object keys will be included.
         * If a depth limit is provided, objects and arrays at that depth will
         * be stringified as empty.
         * @param o {MIXED} any arbitrary object to convert to JSON string
         * @param w {Array} (optional) whitelist of acceptable object keys to include
         * @param d {number} (optional) depth limit to recurse objects/arrays (practical minimum 1)
         * @return {string} JSON string representation of the input
         * @method stringify
         * @static
         * @public
         */
        stringify : function (o,w,d) {

            var l = YAHOO.lang,

                // Regex used to encode strings as safe JSON values
                str_re = /["\\\x00-\x1f]/g,

                // Character substitution map used by regex to prepare strings
                m = {
                    '\b': '\\b',
                    '\t': '\\t',
                    '\n': '\\n',
                    '\f': '\\f',
                    '\r': '\\r',
                    '"' : '\\"',
                    '\\': '\\\\'
                },

                // Processing stack used to prevent cyclical references
                pstack  = [];

            /**
            * Encode odd characters.  Translated characters are cached.
            * @private
            */
            function _encodeChar(c) {
                if (!m[c]) {
                    var a = c.charCodeAt();
                    m[c] = '\\u00' + Math.floor(a / 16).toString(16) +
                                               (a % 16).toString(16);
                }
                return m[c];
            }

            /**
             * zero pad single digits in dates.
             * @private
             */
            function _zeroPad(v) {
                return v < 10 ? '0' + v : v;
            }

            /**
             * Wrap string values and object keys in double quotes after replacing
             * any odd characters.
             * @private
             */
            function _string(o) {
                return '"' + o.replace(str_re, _encodeChar) + '"';
            }

            /**
             * Worker function.  Fork behavior on data type and recurse objects and
             * arrays per the configured depth.
             * @private
             */
            function _stringify(o,w,d) {
                var t = typeof o,
                    i,len,j, // array iteration
                    k,v,     // object iteration
                    vt,      // typeof v during iteration
                    a;       // composition array for performance over string concat

                // String
                if (t === 'string') {
                    return _string(o);
                }

                // native boolean and Boolean instance
                if (t === 'boolean' || o instanceof Boolean) {
                    return String(o);
                }

                // native number and Number instance
                if (t === 'number' || o instanceof Number) {
                    return isFinite(o) ? String(o) : 'null';
                }

                // Date
                if (o instanceof Date) {
                    return ['"',         o.getUTCFullYear(),  '-',
                                _zeroPad(o.getUTCMonth() + 1),'-',
                                _zeroPad(o.getUTCDate()),     'T',
                                _zeroPad(o.getUTCHours()),    ':',
                                _zeroPad(o.getUTCMinutes()),  ':',
                                _zeroPad(o.getUTCSeconds()),  'Z"'].join('');
                }

                // Array
                if (l.isArray(o)) {
                    // Check for cyclical references
                    for (i = 0, len = pstack.length; i < len; ++i) {
                        if (pstack[i] === o) {
                            return 'null';
                        }
                    }

                    // Add the array to the processing stack
                    pstack[pstack.length] = o;

                    a = [];
                    // Only recurse if we're above depth config
                    if (d > 0) {
                        for (i = 0, len = o.length; i < len; ++i) {
                            a[i] = _stringify(o[i],w,d-1);
                        }
                    }

                    // remove the array from the stack
                    pstack.pop();

                    return '[' + a.join(',') + ']';
                }

                // Object
                if (t === 'object' && o) {
                    // Check for cyclical references
                    for (i = 0, len = pstack.length; i < len; ++i) {
                        if (pstack[i] === o) {
                            return 'null';
                        }
                    }

                    // Add the object to the  processing stack
                    pstack[pstack.length] = o;

                    a = [];
                    // Only recurse if we're above depth config
                    if (d > 0) {

                        // If whitelist provided, take only those keys
                        if (w) {
                            for (i = 0, j = 0, len = w.length; i < len; ++i) {
                                v = o[w[i]];
                                vt = typeof v;

                                // Omit invalid values
                                if (vt !== 'undefined' && vt !== 'function') {
                                    a[j++] = _string(w[i]) + ':' + _stringify(v,w,d-1);
                                }
                            }

                        // Otherwise, take all valid object properties
                        // omitting the prototype chain properties
                        } else {
                            j = 0;
                            for (k in o) {
                                if (typeof k === 'string' && l.hasOwnProperty(o,k)) {
                                    v = o[k];
                                    vt = typeof v;
                                    if (vt !== 'undefined' && vt !== 'function') {
                                        a[j++] = _string(k) + ':' + _stringify(v,w,d-1);
                                    }
                                }
                            }
                        }
                    }

                    // Remove the object from processing stack
                    pstack.pop();

                    return '{' + a.join(',') + '}';
                }

                return 'null';
            }

            // process the input
            d = d >= 0 ? d : 1/0;  // Default depth to POSITIVE_INFINITY
            return _stringify(o,w,d);
        }
    };

Copyright © 2007 Yahoo! Inc. All rights reserved.