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