fromJson.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.secure.fromJson"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.secure.fromJson"] = true;
  8. dojo.provide("dojox.secure.fromJson");
  9. // Used with permission from Mike Samuel of Google (has CCLA), from the json-sans-eval project:
  10. // http://code.google.com/p/json-sans-eval/
  11. // Mike Samuel <mikesamuel@gmail.com>
  12. dojox.secure.fromJson = typeof JSON != "undefined" ? JSON.parse :
  13. // summary:
  14. // Parses a string of well-formed JSON text.
  15. // description:
  16. // Parses a string of well-formed JSON text. If the input is not well-formed,
  17. // then behavior is undefined, but it is
  18. // deterministic and is guaranteed not to modify any object other than its
  19. // return value.
  20. //
  21. // This does not use `eval` so is less likely to have obscure security bugs than
  22. // json2.js.
  23. // It is optimized for speed, so is much faster than json_parse.js.
  24. //
  25. // This library should be used whenever security is a concern (when JSON may
  26. // come from an untrusted source), speed is a concern, and erroring on malformed
  27. // JSON is *not* a concern.
  28. //
  29. // json2.js is very fast, but potentially insecure since it calls `eval` to
  30. // parse JSON data, so an attacker might be able to supply strange JS that
  31. // looks like JSON, but that executes arbitrary javascript.
  32. //
  33. // To configure dojox.secure.fromJson as the JSON parser for all Dojo
  34. // JSON parsing, simply do:
  35. // | dojo.require("dojox.secure.fromJson");
  36. // | dojo.fromJson = dojox.secure.fromJson;
  37. // or alternately you could configure dojox.secure.fromJson to only handle
  38. // XHR responses:
  39. // | dojo._contentHandlers.json = function(xhr){
  40. // | return dojox.secure.fromJson.fromJson(xhr.responseText);
  41. // | };
  42. //
  43. // json: String
  44. // per RFC 4627
  45. // optReviver: Function (this:Object, string, *)
  46. // optional function
  47. // that reworks JSON objects post-parse per Chapter 15.12 of EcmaScript3.1.
  48. // If supplied, the function is called with a string key, and a value.
  49. // The value is the property of 'this'. The reviver should return
  50. // the value to use in its place. So if dates were serialized as
  51. // {@code { "type": "Date", "time": 1234 }}, then a reviver might look like
  52. // {@code
  53. // function (key, value) {
  54. // if (value && typeof value === 'object' && 'Date' === value.type) {
  55. // return new Date(value.time);
  56. // } else {
  57. // return value;
  58. // }
  59. // }}.
  60. // If the reviver returns {@code undefined} then the property named by key
  61. // will be deleted from its container.
  62. // {@code this} is bound to the object containing the specified property.
  63. // returns: {Object|Array}
  64. (function () {
  65. var number
  66. = '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)';
  67. var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]'
  68. + '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';
  69. var string = '(?:\"' + oneChar + '*\")';
  70. // Will match a value in a well-formed JSON file.
  71. // If the input is not well-formed, may match strangely, but not in an unsafe
  72. // way.
  73. // Since this only matches value tokens, it does not match whitespace, colons,
  74. // or commas.
  75. var jsonToken = new RegExp(
  76. '(?:false|true|null|[\\{\\}\\[\\]]'
  77. + '|' + number
  78. + '|' + string
  79. + ')', 'g');
  80. // Matches escape sequences in a string literal
  81. var escapeSequence = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g');
  82. // Decodes escape sequences in object literals
  83. var escapes = {
  84. '"': '"',
  85. '/': '/',
  86. '\\': '\\',
  87. 'b': '\b',
  88. 'f': '\f',
  89. 'n': '\n',
  90. 'r': '\r',
  91. 't': '\t'
  92. };
  93. function unescapeOne(_, ch, hex) {
  94. return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16));
  95. }
  96. // A non-falsy value that coerces to the empty string when used as a key.
  97. var EMPTY_STRING = new String('');
  98. var SLASH = '\\';
  99. // Constructor to use based on an open token.
  100. var firstTokenCtors = { '{': Object, '[': Array };
  101. var hop = Object.hasOwnProperty;
  102. return function (json, opt_reviver) {
  103. // Split into tokens
  104. var toks = json.match(jsonToken);
  105. // Construct the object to return
  106. var result;
  107. var tok = toks[0];
  108. var topLevelPrimitive = false;
  109. if ('{' === tok) {
  110. result = {};
  111. } else if ('[' === tok) {
  112. result = [];
  113. } else {
  114. // The RFC only allows arrays or objects at the top level, but the JSON.parse
  115. // defined by the EcmaScript 5 draft does allow strings, booleans, numbers, and null
  116. // at the top level.
  117. result = [];
  118. topLevelPrimitive = true;
  119. }
  120. // If undefined, the key in an object key/value record to use for the next
  121. // value parsed.
  122. var key;
  123. // Loop over remaining tokens maintaining a stack of uncompleted objects and
  124. // arrays.
  125. var stack = [result];
  126. for (var i = 1 - topLevelPrimitive, n = toks.length; i < n; ++i) {
  127. tok = toks[i];
  128. var cont;
  129. switch (tok.charCodeAt(0)) {
  130. default: // sign or digit
  131. cont = stack[0];
  132. cont[key || cont.length] = +(tok);
  133. key = void 0;
  134. break;
  135. case 0x22: // '"'
  136. tok = tok.substring(1, tok.length - 1);
  137. if (tok.indexOf(SLASH) !== -1) {
  138. tok = tok.replace(escapeSequence, unescapeOne);
  139. }
  140. cont = stack[0];
  141. if (!key) {
  142. if (cont instanceof Array) {
  143. key = cont.length;
  144. } else {
  145. key = tok || EMPTY_STRING; // Use as key for next value seen.
  146. break;
  147. }
  148. }
  149. cont[key] = tok;
  150. key = void 0;
  151. break;
  152. case 0x5b: // '['
  153. cont = stack[0];
  154. stack.unshift(cont[key || cont.length] = []);
  155. key = void 0;
  156. break;
  157. case 0x5d: // ']'
  158. stack.shift();
  159. break;
  160. case 0x66: // 'f'
  161. cont = stack[0];
  162. cont[key || cont.length] = false;
  163. key = void 0;
  164. break;
  165. case 0x6e: // 'n'
  166. cont = stack[0];
  167. cont[key || cont.length] = null;
  168. key = void 0;
  169. break;
  170. case 0x74: // 't'
  171. cont = stack[0];
  172. cont[key || cont.length] = true;
  173. key = void 0;
  174. break;
  175. case 0x7b: // '{'
  176. cont = stack[0];
  177. stack.unshift(cont[key || cont.length] = {});
  178. key = void 0;
  179. break;
  180. case 0x7d: // '}'
  181. stack.shift();
  182. break;
  183. }
  184. }
  185. // Fail if we've got an uncompleted object.
  186. if (topLevelPrimitive) {
  187. if (stack.length !== 1) { throw new Error(); }
  188. result = result[0];
  189. } else {
  190. if (stack.length) { throw new Error(); }
  191. }
  192. if (opt_reviver) {
  193. // Based on walk as implemented in http://www.json.org/json2.js
  194. var walk = function (holder, key) {
  195. var value = holder[key];
  196. if (value && typeof value === 'object') {
  197. var toDelete = null;
  198. for (var k in value) {
  199. if (hop.call(value, k) && value !== holder) {
  200. // Recurse to properties first. This has the effect of causing
  201. // the reviver to be called on the object graph depth-first.
  202. // Since 'this' is bound to the holder of the property, the
  203. // reviver can access sibling properties of k including ones
  204. // that have not yet been revived.
  205. // The value returned by the reviver is used in place of the
  206. // current value of property k.
  207. // If it returns undefined then the property is deleted.
  208. var v = walk(value, k);
  209. if (v !== void 0) {
  210. value[k] = v;
  211. } else {
  212. // Deleting properties inside the loop has vaguely defined
  213. // semantics in ES3 and ES3.1.
  214. if (!toDelete) { toDelete = []; }
  215. toDelete.push(k);
  216. }
  217. }
  218. }
  219. if (toDelete) {
  220. for (var i = toDelete.length; --i >= 0;) {
  221. delete value[toDelete[i]];
  222. }
  223. }
  224. }
  225. return opt_reviver.call(holder, key, value);
  226. };
  227. result = walk({ '': result }, '');
  228. }
  229. return result;
  230. };
  231. })();
  232. }