2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
8
* Provides methods to parse JSON strings and convert objects to JSON strings.
13
YAHOO.lang.JSON = (function () {
18
* Replace certain Unicode characters that JavaScript may handle incorrectly
19
* during eval--either by deleting them or treating them as line
20
* endings--with escape sequences.
21
* IMPORTANT NOTE: This regex will be used to modify the input if a match is
23
* @property _UNICODE_EXCEPTIONS
27
_UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
30
* First step in the validation. Regex used to replace all escape
31
* sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
37
_ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
40
* Second step in the validation. Regex used to replace all simple
41
* values with ']' characters.
47
_VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
50
* Third step in the validation. Regex used to remove all open square
51
* brackets following a colon, comma, or at the beginning of the string.
57
_BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
60
* Final step in the validation. Regex used to test the string left after
61
* all previous replacements for invalid characters.
67
_INVALID = /^[\],:{}\s]*$/,
70
* Regex used to replace special characters in strings for JSON
72
* @property _SPECIAL_CHARS
77
_SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
80
* Character substitution map for common escapes and special characters.
97
* Traverses nested objects, applying a filter or reviver function to
98
* each value. The value returned from the function will replace the
99
* original value in the key:value pair. If the value returned is
100
* undefined, the key will be omitted from the returned object.
102
* @param data {MIXED} Any JavaScript data
103
* @param reviver {Function} filter or mutation function
104
* @return {MIXED} The results of the filtered/mutated data structure
107
function _revive(data, reviver) {
108
var walk = function (o,key) {
109
var k,v,value = o[key];
110
if (value && typeof value === 'object') {
112
if (l.hasOwnProperty(value,k)) {
114
if (v === undefined) {
122
return reviver.call(o,key,value);
125
return typeof reviver === 'function' ? walk({'':data},'') : data;
129
* Escapes a special character to a safe Unicode representation
131
* @param c {String} single character to escape
132
* @return {String} safe Unicode escape
136
_CHARS[c] = '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
142
* Replace certain Unicode characters that may be handled incorrectly by
143
* some browser implementations.
145
* @param s {String} parse input
146
* @return {String} sanitized JSON string ready to be validated/parsed
149
function _prepare(s) {
150
return s.replace(_UNICODE_EXCEPTIONS, _char);
154
* Four step determination whether a string is valid JSON. In three steps,
155
* escape sequences, safe values, and properly placed open square brackets
156
* are replaced with placeholders or removed. Then in the final step, the
157
* result of all these replacements is checked for invalid characters.
159
* @param str {String} JSON string to be tested
160
* @return {boolean} is the string safe for eval?
163
function _isValid(str) {
164
return l.isString(str) &&
166
replace(_ESCAPES,'@').
167
replace(_VALUES,']').
168
replace(_BRACKETS,''));
172
* Enclose escaped strings in quotes
174
* @param s {String} string to wrap
175
* @return {String} '"'+s+'"' after s has had special characters escaped
178
function _string(s) {
179
return '"' + s.replace(_SPECIAL_CHARS, _char) + '"';
183
* Worker function used by public stringify.
185
* @param h {Object} object holding the key
186
* @param key {String} String key in object h to serialize
187
* @param depth {Number} depth to serialize
188
* @param w {Array|Function} array of whitelisted keys OR replacer function
189
* @param pstack {Array} used to protect against recursion
190
* @return {String} serialized version of o
192
function _stringify(h,key,d,w,pstack) {
193
var o = typeof w === 'function' ? w.call(h,key,h[key]) : h[key],
194
i,len,j, // array iteration
195
k,v, // object iteration
196
isArray, // forking in typeof 'object'
197
a; // composition array for performance over string concat
199
if (o instanceof Date) {
200
o = l.JSON.dateToString(o);
201
} else if (o instanceof String || o instanceof Boolean || o instanceof Number) {
206
case 'string' : return _string(o);
207
case 'number' : return isFinite(o) ? String(o) : 'null';
208
case 'boolean': return String(o);
215
// Check for cyclical references
216
for (i = pstack.length - 1; i >= 0; --i) {
217
if (pstack[i] === o) {
222
// Add the object to the processing stack
223
pstack[pstack.length] = o;
226
isArray = l.isArray(o);
228
// Only recurse if we're above depth config
232
for (i = o.length - 1; i >= 0; --i) {
233
a[i] = _stringify(o,i,d-1,w,pstack) || 'null';
239
// Use whitelist keys if provided as an array
241
for (i = 0, len = w.length; i < len; ++i) {
243
v = _stringify(o,k,d-1,w,pstack);
245
a[j++] = _string(k) + ':' + v;
250
if (typeof k === 'string' && l.hasOwnProperty(o,k)) {
251
v = _stringify(o,k,d-1,w,pstack);
253
a[j++] = _string(k) + ':' + v;
259
// sort object keys for easier readability
264
// remove the object from the stack
267
return isArray ? '['+a.join(',')+']' : '{'+a.join(',')+'}';
270
return undefined; // invalid input
273
// Return the public API
276
* Four step determination whether a string is valid JSON. In three steps,
277
* escape sequences, safe values, and properly placed open square brackets
278
* are replaced with placeholders or removed. Then in the final step, the
279
* result of all these replacements is checked for invalid characters.
281
* @param str {String} JSON string to be tested
282
* @return {boolean} is the string safe for eval?
285
isValid : function (s) {
286
return _isValid(_prepare(s));
290
* Parse a JSON string, returning the native JavaScript representation.
291
* Only minor modifications from http://www.json.org/json2.js.
292
* @param s {string} JSON string data
293
* @param reviver {function} (optional) function(k,v) passed each key:value
294
* pair of object literals, allowing pruning or altering values
295
* @return {MIXED} the native JavaScript representation of the JSON string
296
* @throws SyntaxError
300
parse : function (s,reviver) {
306
// Eval the text into a JavaScript data structure, apply the
307
// reviver function if provided, and return
308
return _revive( eval('(' + s + ')'), reviver );
311
// The text is not valid JSON
312
throw new SyntaxError('parseJSON');
316
* Converts an arbitrary value to a JSON string representation.
317
* Cyclical object or array references are replaced with null.
318
* If a whitelist is provided, only matching object keys will be included.
319
* If a depth limit is provided, objects and arrays at that depth will
320
* be stringified as empty.
322
* @param o {MIXED} any arbitrary object to convert to JSON string
323
* @param w {Array|Function} (optional) whitelist of acceptable object keys to include OR a function(value,key) to alter values before serialization
324
* @param d {number} (optional) depth limit to recurse objects/arrays (practical minimum 1)
325
* @return {string} JSON string representation of the input
328
stringify : function (o,w,d) {
329
if (o !== undefined) {
330
// Ensure whitelist keys are unique (bug 2110391)
333
var uniq=[],map={},v,i,j,len;
334
for (i=0,j=0,len=a.length; i<len; ++i) {
336
if (typeof v === 'string' && map[v] === undefined) {
337
uniq[(map[v] = j++)] = v;
344
// Default depth to POSITIVE_INFINITY
345
d = d >= 0 ? d : 1/0;
348
return _stringify({'':o},'',d,w,[]);
355
* Serializes a Date instance as a UTC date string. Used internally by
356
* stringify. Override this method if you need Dates serialized in a
358
* @method dateToString
359
* @param d {Date} The Date to serialize
360
* @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
363
dateToString : function (d) {
364
function _zeroPad(v) {
365
return v < 10 ? '0' + v : v;
368
return d.getUTCFullYear() + '-' +
369
_zeroPad(d.getUTCMonth() + 1) + '-' +
370
_zeroPad(d.getUTCDate()) + 'T' +
371
_zeroPad(d.getUTCHours()) + ':' +
372
_zeroPad(d.getUTCMinutes()) + ':' +
373
_zeroPad(d.getUTCSeconds()) + 'Z';
377
* Reconstitute Date instances from the default JSON UTC serialization.
378
* Reference this from a reviver function to rebuild Dates during the
380
* @method stringToDate
381
* @param str {String} String serialization of a Date
384
stringToDate : function (str) {
385
if (/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.test(str)) {
387
d.setUTCFullYear(RegExp.$1, (RegExp.$2|0)-1, RegExp.$3);
388
d.setUTCHours(RegExp.$4, RegExp.$5, RegExp.$6);
396
YAHOO.register("json", YAHOO.lang.JSON, {version: "2.7.0", build: "1799"});