JSON.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * Note:
  7. * This work draws heavily from the public domain JSON serializer/deserializer
  8. * at http://www.json.org/json.js. Rewritten so that it doesn't modify
  9. * basic data prototypes.
  10. */
  11. /**
  12. * @requires OpenLayers/Format.js
  13. */
  14. /**
  15. * Class: OpenLayers.Format.JSON
  16. * A parser to read/write JSON safely. Create a new instance with the
  17. * <OpenLayers.Format.JSON> constructor.
  18. *
  19. * Inherits from:
  20. * - <OpenLayers.Format>
  21. */
  22. OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
  23. /**
  24. * APIProperty: indent
  25. * {String} For "pretty" printing, the indent string will be used once for
  26. * each indentation level.
  27. */
  28. indent: " ",
  29. /**
  30. * APIProperty: space
  31. * {String} For "pretty" printing, the space string will be used after
  32. * the ":" separating a name/value pair.
  33. */
  34. space: " ",
  35. /**
  36. * APIProperty: newline
  37. * {String} For "pretty" printing, the newline string will be used at the
  38. * end of each name/value pair or array item.
  39. */
  40. newline: "\n",
  41. /**
  42. * Property: level
  43. * {Integer} For "pretty" printing, this is incremented/decremented during
  44. * serialization.
  45. */
  46. level: 0,
  47. /**
  48. * Property: pretty
  49. * {Boolean} Serialize with extra whitespace for structure. This is set
  50. * by the <write> method.
  51. */
  52. pretty: false,
  53. /**
  54. * Property: nativeJSON
  55. * {Boolean} Does the browser support native json?
  56. */
  57. nativeJSON: (function() {
  58. return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
  59. })(),
  60. /**
  61. * Constructor: OpenLayers.Format.JSON
  62. * Create a new parser for JSON.
  63. *
  64. * Parameters:
  65. * options - {Object} An optional object whose properties will be set on
  66. * this instance.
  67. */
  68. /**
  69. * APIMethod: read
  70. * Deserialize a json string.
  71. *
  72. * Parameters:
  73. * json - {String} A JSON string
  74. * filter - {Function} A function which will be called for every key and
  75. * value at every level of the final result. Each value will be
  76. * replaced by the result of the filter function. This can be used to
  77. * reform generic objects into instances of classes, or to transform
  78. * date strings into Date objects.
  79. *
  80. * Returns:
  81. * {Object} An object, array, string, or number .
  82. */
  83. read: function(json, filter) {
  84. var object;
  85. if (this.nativeJSON) {
  86. object = JSON.parse(json, filter);
  87. } else try {
  88. /**
  89. * Parsing happens in three stages. In the first stage, we run the
  90. * text against a regular expression which looks for non-JSON
  91. * characters. We are especially concerned with '()' and 'new'
  92. * because they can cause invocation, and '=' because it can
  93. * cause mutation. But just to be safe, we will reject all
  94. * unexpected characters.
  95. */
  96. if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
  97. replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
  98. replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
  99. /**
  100. * In the second stage we use the eval function to compile the
  101. * text into a JavaScript structure. The '{' operator is
  102. * subject to a syntactic ambiguity in JavaScript - it can
  103. * begin a block or an object literal. We wrap the text in
  104. * parens to eliminate the ambiguity.
  105. */
  106. object = eval('(' + json + ')');
  107. /**
  108. * In the optional third stage, we recursively walk the new
  109. * structure, passing each name/value pair to a filter
  110. * function for possible transformation.
  111. */
  112. if(typeof filter === 'function') {
  113. function walk(k, v) {
  114. if(v && typeof v === 'object') {
  115. for(var i in v) {
  116. if(v.hasOwnProperty(i)) {
  117. v[i] = walk(i, v[i]);
  118. }
  119. }
  120. }
  121. return filter(k, v);
  122. }
  123. object = walk('', object);
  124. }
  125. }
  126. } catch(e) {
  127. // Fall through if the regexp test fails.
  128. }
  129. if(this.keepData) {
  130. this.data = object;
  131. }
  132. return object;
  133. },
  134. /**
  135. * APIMethod: write
  136. * Serialize an object into a JSON string.
  137. *
  138. * Parameters:
  139. * value - {String} The object, array, string, number, boolean or date
  140. * to be serialized.
  141. * pretty - {Boolean} Structure the output with newlines and indentation.
  142. * Default is false.
  143. *
  144. * Returns:
  145. * {String} The JSON string representation of the input value.
  146. */
  147. write: function(value, pretty) {
  148. this.pretty = !!pretty;
  149. var json = null;
  150. var type = typeof value;
  151. if(this.serialize[type]) {
  152. try {
  153. json = (!this.pretty && this.nativeJSON) ?
  154. JSON.stringify(value) :
  155. this.serialize[type].apply(this, [value]);
  156. } catch(err) {
  157. OpenLayers.Console.error("Trouble serializing: " + err);
  158. }
  159. }
  160. return json;
  161. },
  162. /**
  163. * Method: writeIndent
  164. * Output an indentation string depending on the indentation level.
  165. *
  166. * Returns:
  167. * {String} An appropriate indentation string.
  168. */
  169. writeIndent: function() {
  170. var pieces = [];
  171. if(this.pretty) {
  172. for(var i=0; i<this.level; ++i) {
  173. pieces.push(this.indent);
  174. }
  175. }
  176. return pieces.join('');
  177. },
  178. /**
  179. * Method: writeNewline
  180. * Output a string representing a newline if in pretty printing mode.
  181. *
  182. * Returns:
  183. * {String} A string representing a new line.
  184. */
  185. writeNewline: function() {
  186. return (this.pretty) ? this.newline : '';
  187. },
  188. /**
  189. * Method: writeSpace
  190. * Output a string representing a space if in pretty printing mode.
  191. *
  192. * Returns:
  193. * {String} A space.
  194. */
  195. writeSpace: function() {
  196. return (this.pretty) ? this.space : '';
  197. },
  198. /**
  199. * Property: serialize
  200. * Object with properties corresponding to the serializable data types.
  201. * Property values are functions that do the actual serializing.
  202. */
  203. serialize: {
  204. /**
  205. * Method: serialize.object
  206. * Transform an object into a JSON string.
  207. *
  208. * Parameters:
  209. * object - {Object} The object to be serialized.
  210. *
  211. * Returns:
  212. * {String} A JSON string representing the object.
  213. */
  214. 'object': function(object) {
  215. // three special objects that we want to treat differently
  216. if(object == null) {
  217. return "null";
  218. }
  219. if(object.constructor == Date) {
  220. return this.serialize.date.apply(this, [object]);
  221. }
  222. if(object.constructor == Array) {
  223. return this.serialize.array.apply(this, [object]);
  224. }
  225. var pieces = ['{'];
  226. this.level += 1;
  227. var key, keyJSON, valueJSON;
  228. var addComma = false;
  229. for(key in object) {
  230. if(object.hasOwnProperty(key)) {
  231. // recursive calls need to allow for sub-classing
  232. keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
  233. [key, this.pretty]);
  234. valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
  235. [object[key], this.pretty]);
  236. if(keyJSON != null && valueJSON != null) {
  237. if(addComma) {
  238. pieces.push(',');
  239. }
  240. pieces.push(this.writeNewline(), this.writeIndent(),
  241. keyJSON, ':', this.writeSpace(), valueJSON);
  242. addComma = true;
  243. }
  244. }
  245. }
  246. this.level -= 1;
  247. pieces.push(this.writeNewline(), this.writeIndent(), '}');
  248. return pieces.join('');
  249. },
  250. /**
  251. * Method: serialize.array
  252. * Transform an array into a JSON string.
  253. *
  254. * Parameters:
  255. * array - {Array} The array to be serialized
  256. *
  257. * Returns:
  258. * {String} A JSON string representing the array.
  259. */
  260. 'array': function(array) {
  261. var json;
  262. var pieces = ['['];
  263. this.level += 1;
  264. for(var i=0, len=array.length; i<len; ++i) {
  265. // recursive calls need to allow for sub-classing
  266. json = OpenLayers.Format.JSON.prototype.write.apply(this,
  267. [array[i], this.pretty]);
  268. if(json != null) {
  269. if(i > 0) {
  270. pieces.push(',');
  271. }
  272. pieces.push(this.writeNewline(), this.writeIndent(), json);
  273. }
  274. }
  275. this.level -= 1;
  276. pieces.push(this.writeNewline(), this.writeIndent(), ']');
  277. return pieces.join('');
  278. },
  279. /**
  280. * Method: serialize.string
  281. * Transform a string into a JSON string.
  282. *
  283. * Parameters:
  284. * string - {String} The string to be serialized
  285. *
  286. * Returns:
  287. * {String} A JSON string representing the string.
  288. */
  289. 'string': function(string) {
  290. // If the string contains no control characters, no quote characters, and no
  291. // backslash characters, then we can simply slap some quotes around it.
  292. // Otherwise we must also replace the offending characters with safe
  293. // sequences.
  294. var m = {
  295. '\b': '\\b',
  296. '\t': '\\t',
  297. '\n': '\\n',
  298. '\f': '\\f',
  299. '\r': '\\r',
  300. '"' : '\\"',
  301. '\\': '\\\\'
  302. };
  303. if(/["\\\x00-\x1f]/.test(string)) {
  304. return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
  305. var c = m[b];
  306. if(c) {
  307. return c;
  308. }
  309. c = b.charCodeAt();
  310. return '\\u00' +
  311. Math.floor(c / 16).toString(16) +
  312. (c % 16).toString(16);
  313. }) + '"';
  314. }
  315. return '"' + string + '"';
  316. },
  317. /**
  318. * Method: serialize.number
  319. * Transform a number into a JSON string.
  320. *
  321. * Parameters:
  322. * number - {Number} The number to be serialized.
  323. *
  324. * Returns:
  325. * {String} A JSON string representing the number.
  326. */
  327. 'number': function(number) {
  328. return isFinite(number) ? String(number) : "null";
  329. },
  330. /**
  331. * Method: serialize.boolean
  332. * Transform a boolean into a JSON string.
  333. *
  334. * Parameters:
  335. * bool - {Boolean} The boolean to be serialized.
  336. *
  337. * Returns:
  338. * {String} A JSON string representing the boolean.
  339. */
  340. 'boolean': function(bool) {
  341. return String(bool);
  342. },
  343. /**
  344. * Method: serialize.object
  345. * Transform a date into a JSON string.
  346. *
  347. * Parameters:
  348. * date - {Date} The date to be serialized.
  349. *
  350. * Returns:
  351. * {String} A JSON string representing the date.
  352. */
  353. 'date': function(date) {
  354. function format(number) {
  355. // Format integers to have at least two digits.
  356. return (number < 10) ? '0' + number : number;
  357. }
  358. return '"' + date.getFullYear() + '-' +
  359. format(date.getMonth() + 1) + '-' +
  360. format(date.getDate()) + 'T' +
  361. format(date.getHours()) + ':' +
  362. format(date.getMinutes()) + ':' +
  363. format(date.getSeconds()) + '"';
  364. }
  365. },
  366. CLASS_NAME: "OpenLayers.Format.JSON"
  367. });