json.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. define(["./has"], function(has){
  2. "use strict";
  3. var hasJSON = typeof JSON != "undefined";
  4. has.add("json-parse", hasJSON); // all the parsers work fine
  5. // Firefox 3.5/Gecko 1.9 fails to use replacer in stringify properly https://bugzilla.mozilla.org/show_bug.cgi?id=509184
  6. has.add("json-stringify", hasJSON && JSON.stringify({a:0}, function(k,v){return v||1;}) == '{"a":1}');
  7. /*=====
  8. return {
  9. // summary:
  10. // Functions to parse and serialize JSON
  11. parse: function(str, strict){
  12. // summary:
  13. // Parses a [JSON](http://json.org) string to return a JavaScript object.
  14. // description:
  15. // This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
  16. // Throws for invalid JSON strings. This delegates to eval() if native JSON
  17. // support is not available. By default this will evaluate any valid JS expression.
  18. // With the strict parameter set to true, the parser will ensure that only
  19. // valid JSON strings are parsed (otherwise throwing an error). Without the strict
  20. // parameter, the content passed to this method must come
  21. // from a trusted source.
  22. // str:
  23. // a string literal of a JSON item, for instance:
  24. // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
  25. // strict:
  26. // When set to true, this will ensure that only valid, secure JSON is ever parsed.
  27. // Make sure this is set to true for untrusted content. Note that on browsers/engines
  28. // without native JSON support, setting this to true will run slower.
  29. },
  30. stringify: function(value, replacer, spacer){
  31. // summary:
  32. // Returns a [JSON](http://json.org) serialization of an object.
  33. // description:
  34. // Returns a [JSON](http://json.org) serialization of an object.
  35. // This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
  36. // Note that this doesn't check for infinite recursion, so don't do that!
  37. // value:
  38. // A value to be serialized.
  39. // replacer:
  40. // A replacer function that is called for each value and can return a replacement
  41. // spacer:
  42. // A spacer string to be used for pretty printing of JSON
  43. // example:
  44. // simple serialization of a trivial object
  45. // | define(["dojo/json"], function(JSON){
  46. // | var jsonStr = JSON.stringify({ howdy: "stranger!", isStrange: true });
  47. // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr);
  48. }
  49. };
  50. =====*/
  51. if(has("json-stringify")){
  52. return JSON;
  53. }else{
  54. var escapeString = function(/*String*/str){
  55. // summary:
  56. // Adds escape sequences for non-visual characters, double quote and
  57. // backslash and surrounds with double quotes to form a valid string
  58. // literal.
  59. return ('"' + str.replace(/(["\\])/g, '\\$1') + '"').
  60. replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n").
  61. replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string
  62. };
  63. return {
  64. parse: has("json-parse") ? JSON.parse : function(str, strict){
  65. if(strict && !/^([\s\[\{]*(?:"(?:\\.|[^"])*"|-?\d[\d\.]*(?:[Ee][+-]?\d+)?|null|true|false|)[\s\]\}]*(?:,|:|$))+$/.test(str)){
  66. throw new SyntaxError("Invalid characters in JSON");
  67. }
  68. return eval('(' + str + ')');
  69. },
  70. stringify: function(value, replacer, spacer){
  71. var undef;
  72. if(typeof replacer == "string"){
  73. spacer = replacer;
  74. replacer = null;
  75. }
  76. function stringify(it, indent, key){
  77. if(replacer){
  78. it = replacer(key, it);
  79. }
  80. var val, objtype = typeof it;
  81. if(objtype == "number"){
  82. return isFinite(it) ? it + "" : "null";
  83. }
  84. if(objtype == "boolean"){
  85. return it + "";
  86. }
  87. if(it === null){
  88. return "null";
  89. }
  90. if(typeof it == "string"){
  91. return escapeString(it);
  92. }
  93. if(objtype == "function" || objtype == "undefined"){
  94. return undef; // undefined
  95. }
  96. // short-circuit for objects that support "json" serialization
  97. // if they return "self" then just pass-through...
  98. if(typeof it.toJSON == "function"){
  99. return stringify(it.toJSON(key), indent, key);
  100. }
  101. if(it instanceof Date){
  102. return '"{FullYear}-{Month+}-{Date}T{Hours}:{Minutes}:{Seconds}Z"'.replace(/\{(\w+)(\+)?\}/g, function(t, prop, plus){
  103. var num = it["getUTC" + prop]() + (plus ? 1 : 0);
  104. return num < 10 ? "0" + num : num;
  105. });
  106. }
  107. if(it.valueOf() !== it){
  108. // primitive wrapper, try again unwrapped:
  109. return stringify(it.valueOf(), indent, key);
  110. }
  111. var nextIndent= spacer ? (indent + spacer) : "";
  112. /* we used to test for DOM nodes and throw, but FF serializes them as {}, so cross-browser consistency is probably not efficiently attainable */
  113. var sep = spacer ? " " : "";
  114. var newLine = spacer ? "\n" : "";
  115. // array
  116. if(it instanceof Array){
  117. var itl = it.length, res = [];
  118. for(key = 0; key < itl; key++){
  119. var obj = it[key];
  120. val = stringify(obj, nextIndent, key);
  121. if(typeof val != "string"){
  122. val = "null";
  123. }
  124. res.push(newLine + nextIndent + val);
  125. }
  126. return "[" + res.join(",") + newLine + indent + "]";
  127. }
  128. // generic object code path
  129. var output = [];
  130. for(key in it){
  131. var keyStr;
  132. if(it.hasOwnProperty(key)){
  133. if(typeof key == "number"){
  134. keyStr = '"' + key + '"';
  135. }else if(typeof key == "string"){
  136. keyStr = escapeString(key);
  137. }else{
  138. // skip non-string or number keys
  139. continue;
  140. }
  141. val = stringify(it[key], nextIndent, key);
  142. if(typeof val != "string"){
  143. // skip non-serializable values
  144. continue;
  145. }
  146. // At this point, the most non-IE browsers don't get in this branch
  147. // (they have native JSON), so push is definitely the way to
  148. output.push(newLine + nextIndent + keyStr + ":" + sep + val);
  149. }
  150. }
  151. return "{" + output.join(",") + newLine + indent + "}"; // String
  152. }
  153. return stringify(value, "", "");
  154. }
  155. };
  156. }
  157. });