1 /* 2 http://www.JSON.org/json2.js 3 2008-06-04 4 5 Public Domain. 6 7 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 9 See http://www.JSON.org/js.html 10 11 This file creates a global JSON object containing two methods: stringify 12 and parse. 13 14 15 JSON.stringify(value, replacer, space, linebreak) 16 value any JavaScript value, usually an object or array. 17 18 replacer an optional parameter that determines how object 19 values are stringified for objects without a toJSON 20 method. It can be a function or an array. 21 22 space an optional parameter that specifies the indentation 23 of nested structures. If it is omitted, the text will 24 be packed without extra whitespace. If it is a number, 25 it will specify the number of spaces to indent at each 26 level. If it is a string (such as '\t' or ' '), 27 it contains the characters used to indent at each level. 28 29 linebreak an optional parameter that specifies the text used to 30 break lines, such as '<br>' or '\r\n'. It is used with 31 the space parameter. The default is '\n'. 32 33 This method produces a JSON text from a JavaScript value. 34 35 When an object value is found, if the object contains a toJSON 36 method, its toJSON method will be called and the result will be 37 stringified. A toJSON method does not serialize: it returns the 38 value represented by the name/value pair that should be serialized, 39 or undefined if nothing should be serialized. The toJSON method 40 will be passed the key associated with the value, and this will be 41 bound to the object holding the key. 42 43 For example, this would serialize Dates as ISO strings. 44 45 Date.prototype.toJSON = function (key) { 46 function f(n) { 47 // Format integers to have at least two digits. 48 return n < 10 ? '0' + n : n; 49 } 50 51 return this.getUTCFullYear() + '-' + 52 f(this.getUTCMonth() + 1) + '-' + 53 f(this.getUTCDate()) + 'T' + 54 f(this.getUTCHours()) + ':' + 55 f(this.getUTCMinutes()) + ':' + 56 f(this.getUTCSeconds()) + 'Z'; 57 }; 58 59 You can provide an optional replacer method. It will be passed the 60 key and value of each member, with this bound to the containing 61 object. The value that is returned from your method will be 62 serialized. If your method returns undefined, then the member will 63 be excluded from the serialization. 64 65 If the replacer parameter is an array, then it will be used to 66 select the members to be serialized. It filters the results such 67 that only members with keys listed in the replacer array are 68 stringified. 69 70 Values that do not have JSON representaions, such as undefined or 71 functions, will not be serialized. Such values in objects will be 72 dropped; in arrays they will be replaced with null. You can use 73 a replacer function to replace those with JSON values. 74 JSON.stringify(undefined) returns undefined. 75 76 The optional space parameter produces a stringification of the 77 value that is filled with line breaks and indentation to make it 78 easier to read. 79 80 If the space parameter is a non-empty string, then that string will 81 be used for indentation. If the space parameter is a number, then 82 then indentation will be that many spaces. 83 84 Example: 85 86 text = JSON.stringify(['e', {pluribus: 'unum'}]); 87 // text is '["e",{"pluribus":"unum"}]' 88 89 90 text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 91 // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 92 93 text = JSON.stringify([new Date()], function (key, value) { 94 return this[key] instanceof Date ? 95 'Date(' + this[key] + ')' : value; 96 }); 97 // text is '["Date(---current time---)"]' 98 99 100 JSON.parse(text, reviver) 101 This method parses a JSON text to produce an object or array. 102 It can throw a SyntaxError exception. 103 104 The optional reviver parameter is a function that can filter and 105 transform the results. It receives each of the keys and values, 106 and its return value is used instead of the original value. 107 If it returns what it received, then the structure is not modified. 108 If it returns undefined then the member is deleted. 109 110 Example: 111 112 // Parse the text. Values that look like ISO date strings will 113 // be converted to Date objects. 114 115 myData = JSON.parse(text, function (key, value) { 116 var a; 117 if (typeof value === 'string') { 118 a = 119 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 120 if (a) { 121 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 122 +a[5], +a[6])); 123 } 124 } 125 return value; 126 }); 127 128 myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 129 var d; 130 if (typeof value === 'string' && 131 value.slice(0, 5) === 'Date(' && 132 value.slice(-1) === ')') { 133 d = new Date(value.slice(5, -1)); 134 if (d) { 135 return d; 136 } 137 } 138 return value; 139 }); 140 141 142 This is a reference implementation. You are free to copy, modify, or 143 redistribute. 144 145 This code should be minified before deployment. 146 See http://javascript.crockford.com/jsmin.html 147 148 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY 149 CODE INTO YOUR PAGES. 150 */ 151 152 /*jslint evil: true */ 153 154 /*global JSON */ 155 156 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call, 157 charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, 158 getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, 159 parse, propertyIsEnumerable, prototype, push, replace, slice, stringify, 160 test, toJSON, toString 161 */ 162 163 if (!this.JSON) { 164 165 // Create a JSON object only if one does not already exist. We create the 166 // object in a closure to avoid global variables. 167 168 JSON = function () { 169 170 function f(n) { 171 // Format integers to have at least two digits. 172 return n < 10 ? '0' + n : n; 173 } 174 175 Date.prototype.toJSON = function (key) { 176 177 return this.getUTCFullYear() + '-' + 178 f(this.getUTCMonth() + 1) + '-' + 179 f(this.getUTCDate()) + 'T' + 180 f(this.getUTCHours()) + ':' + 181 f(this.getUTCMinutes()) + ':' + 182 f(this.getUTCSeconds()) + 'Z'; 183 }; 184 185 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 186 escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 187 gap, 188 indent, 189 breaker, 190 meta = { // table of character substitutions 191 '\b': '\\b', 192 '\t': '\\t', 193 '\n': '\\n', 194 '\f': '\\f', 195 '\r': '\\r', 196 '"' : '\\"', 197 '\\': '\\\\' 198 }, 199 rep; 200 201 202 function quote(string) { 203 204 // If the string contains no control characters, no quote characters, and no 205 // backslash characters, then we can safely slap some quotes around it. 206 // Otherwise we must also replace the offending characters with safe escape 207 // sequences. 208 209 escapeable.lastIndex = 0; 210 return escapeable.test(string) ? 211 '"' + string.replace(escapeable, function (a) { 212 var c = meta[a]; 213 if (typeof c === 'string') { 214 return c; 215 } 216 return '\\u' + ('0000' + 217 (+(a.charCodeAt(0))).toString(16)).slice(-4); 218 }) + '"' : 219 '"' + string + '"'; 220 } 221 222 223 function str(key, holder) { 224 225 // Produce a string from holder[key]. 226 227 var i, // The loop counter. 228 k, // The member key. 229 v, // The member value. 230 length, 231 mind = gap, 232 partial, 233 value = holder[key]; 234 235 // If the value has a toJSON method, call it to obtain a replacement value. 236 237 if (value && typeof value === 'object' && 238 typeof value.toJSON === 'function') { 239 value = value.toJSON(key); 240 } 241 242 // If we were called with a replacer function, then call the replacer to 243 // obtain a replacement value. 244 245 if (typeof rep === 'function') { 246 value = rep.call(holder, key, value); 247 } 248 249 // What happens next depends on the value's type. 250 251 switch (typeof value) { 252 case 'string': 253 return quote(value); 254 255 case 'number': 256 257 // JSON numbers must be finite. Encode non-finite numbers as null. 258 259 return isFinite(value) ? String(value) : 'null'; 260 261 case 'boolean': 262 case 'null': 263 264 // If the value is a boolean or null, convert it to a string. Note: 265 // typeof null does not produce 'null'. The case is included here in 266 // the remote chance that this gets fixed someday. 267 268 return String(value); 269 270 // If the type is 'object', we might be dealing with an object or an array or 271 // null. 272 273 case 'object': 274 275 // Due to a specification blunder in ECMAScript, typeof null is 'object', 276 // so watch out for that case. 277 278 if (!value) { 279 return 'null'; 280 } 281 282 // Make an array to hold the partial results of stringifying this object value. 283 284 gap += indent; 285 partial = []; 286 287 // If the object has a dontEnum length property, we'll treat it as an array. 288 289 if (typeof value.length === 'number' && 290 !(value.propertyIsEnumerable('length'))) { 291 292 // The object is an array. Stringify every element. Use null as a placeholder 293 // for non-JSON values. 294 295 length = value.length; 296 for (i = 0; i < length; i += 1) { 297 partial[i] = str(i, value) || 'null'; 298 } 299 300 // Join all of the elements together, separated with commas, and wrap them in 301 // brackets. 302 303 v = partial.length === 0 ? '[]' : 304 gap ? '[' + breaker + gap + 305 partial.join(',' + breaker + gap) + breaker + 306 mind + ']' : 307 '[' + partial.join(',') + ']'; 308 gap = mind; 309 return v; 310 } 311 312 // If the replacer is an array, use it to select the members to be stringified. 313 314 if (rep && typeof rep === 'object') { 315 length = rep.length; 316 for (i = 0; i < length; i += 1) { 317 k = rep[i]; 318 if (typeof k === 'string') { 319 v = str(k, value, rep); 320 if (v) { 321 partial.push(quote(k) + (gap ? ': ' : ':') + v); 322 } 323 } 324 } 325 } else { 326 327 // Otherwise, iterate through all of the keys in the object. 328 329 for (k in value) { 330 if (Object.hasOwnProperty.call(value, k)) { 331 v = str(k, value, rep); 332 if (v) { 333 partial.push(quote(k) + (gap ? ': ' : ':') + v); 334 } 335 } 336 } 337 } 338 339 // Join all of the member texts together, separated with commas, 340 // and wrap them in braces. 341 342 v = partial.length === 0 ? '{}' : 343 gap ? '{' + breaker + gap + 344 partial.join(',' + breaker + gap) + breaker + 345 mind + '}' : 346 '{' + partial.join(',') + '}'; 347 gap = mind; 348 return v; 349 } 350 } 351 352 353 // Return the JSON object containing the stringify, parse, and quote methods. 354 355 return { 356 stringify: function (value, replacer, space, linebreak) { 357 358 // The stringify method takes a value and an optional replacer, and an optional 359 // space parameter, and returns a JSON text. The replacer can be a function 360 // that can replace values, or an array of strings that will select the keys. 361 // A default replacer method can be provided. Use of the space parameter can 362 // produce text that is more easily readable. 363 364 var i; 365 gap = ''; 366 indent = ''; 367 if (space) { 368 369 // If the space parameter is a number, make an indent string containing that 370 // many spaces. 371 372 if (typeof space === 'number') { 373 for (i = 0; i < space; i += 1) { 374 indent += ' '; 375 } 376 377 // If the space parameter is a string, it will be used as the indent string. 378 379 } else if (typeof space === 'string') { 380 indent = space; 381 } 382 breaker = typeof linebreak !== 'string' ? '\n' : linebreak; 383 } 384 385 // If there is a replacer, it must be a function or an array. 386 // Otherwise, throw an error. 387 388 rep = replacer; 389 if (replacer && typeof replacer !== 'function' && 390 (typeof replacer !== 'object' || 391 typeof replacer.length !== 'number')) { 392 throw new Error('JSON.stringify'); 393 } 394 395 // Make a fake root object containing our value under the key of ''. 396 // Return the result of stringifying the value. 397 398 return str('', {'': value}); 399 }, 400 401 402 parse: function (text, reviver) { 403 404 // The parse method takes a text and an optional reviver function, and returns 405 // a JavaScript value if the text is a valid JSON text. 406 407 var j; 408 409 function walk(holder, key) { 410 411 // The walk method is used to recursively walk the resulting structure so 412 // that modifications can be made. 413 414 var k, v, value = holder[key]; 415 if (value && typeof value === 'object') { 416 for (k in value) { 417 if (Object.hasOwnProperty.call(value, k)) { 418 v = walk(value, k); 419 if (v !== undefined) { 420 value[k] = v; 421 } else { 422 delete value[k]; 423 } 424 } 425 } 426 } 427 return reviver.call(holder, key, value); 428 } 429 430 431 // Parsing happens in four stages. In the first stage, we replace certain 432 // Unicode characters with escape sequences. JavaScript handles many characters 433 // incorrectly, either silently deleting them, or treating them as line endings. 434 435 cx.lastIndex = 0; 436 if (cx.test(text)) { 437 text = text.replace(cx, function (a) { 438 return '\\u' + ('0000' + 439 (+(a.charCodeAt(0))).toString(16)).slice(-4); 440 }); 441 } 442 443 // In the second stage, we run the text against 444 // regular expressions that look for non-JSON patterns. We are especially 445 // concerned with '()' and 'new' because they can cause invocation, and '=' 446 // because it can cause mutation. But just to be safe, we want to reject all 447 // unexpected forms. 448 449 // We split the second stage into 4 regexp operations in order to work around 450 // crippling inefficiencies in IE's and Safari's regexp engines. First we 451 // replace all backslash pairs with '@' (a non-JSON character). Second, we 452 // replace all simple value tokens with ']' characters. Third, we delete all 453 // open brackets that follow a colon or comma or that begin the text. Finally, 454 // we look to see that the remaining characters are only whitespace or ']' or 455 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 456 457 if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@'). 458 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). 459 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 460 461 // In the third stage we use the eval function to compile the text into a 462 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 463 // in JavaScript: it can begin a block or an object literal. We wrap the text 464 // in parens to eliminate the ambiguity. 465 466 j = eval('(' + text + ')'); 467 468 // In the optional fourth stage, we recursively walk the new structure, passing 469 // each name/value pair to a reviver function for possible transformation. 470 471 return typeof reviver === 'function' ? 472 walk({'': j}, '') : j; 473 } 474 475 // If the text is not JSON parseable, then a SyntaxError is thrown. 476 477 throw new SyntaxError('JSON.parse'); 478 } 479 }; 480 }(); 481 } 482