| OLD | NEW |
| (Empty) |
| 1 // Copyright 2006 Google Inc. | |
| 2 // All Rights Reserved. | |
| 3 // | |
| 4 // Redistribution and use in source and binary forms, with or without | |
| 5 // modification, are permitted provided that the following conditions | |
| 6 // are met: | |
| 7 // | |
| 8 // * Redistributions of source code must retain the above copyright | |
| 9 // notice, this list of conditions and the following disclaimer. | |
| 10 // * Redistributions in binary form must reproduce the above copyright | |
| 11 // notice, this list of conditions and the following disclaimer in | |
| 12 // the documentation and/or other materials provided with the | |
| 13 // distribution. | |
| 14 // | |
| 15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 18 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 19 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 20 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 21 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 22 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| 23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 24 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
| 25 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| 26 // POSSIBILITY OF SUCH DAMAGE. | |
| 27 | |
| 28 // NOTE: This file has been changed from the one on doctype. The following | |
| 29 // changes were made: | |
| 30 // - Modified unsafeParse() to use new Function() instead of eval() because eval | |
| 31 // is not allowed inside v8 extensions. | |
| 32 // - Modified parse() to delegate to unsafeParse() instead of calling eval() | |
| 33 // directly. | |
| 34 | |
| 35 /** | |
| 36 * @fileoverview JSON utility functions | |
| 37 */ | |
| 38 | |
| 39 | |
| 40 | |
| 41 goog.provide('goog.json'); | |
| 42 goog.provide('goog.json.Serializer'); | |
| 43 | |
| 44 | |
| 45 /** | |
| 46 * Tests if a string is an invalid JSON string. This only ensures that we are | |
| 47 * not using any invalid characters | |
| 48 * @param {string} s The string to test. | |
| 49 * @return {boolean} True if the input is a valid JSON string. | |
| 50 * @private | |
| 51 */ | |
| 52 goog.json.isValid_ = function(s) { | |
| 53 // All empty whitespace is not valid. | |
| 54 if (/^\s*$/.test(s)) { | |
| 55 return false; | |
| 56 } | |
| 57 | |
| 58 // This is taken from http://www.json.org/json2.js which is released to the | |
| 59 // public domain. | |
| 60 // Changes: We dissallow \u2028 Line separator and \u2029 Paragraph separator | |
| 61 // inside strings. We also treat \u2028 and \u2029 as whitespace which they | |
| 62 // are in the RFC but IE and Safari does not match \s to these so we need to | |
| 63 // include them in the reg exps in all places where whitespace is allowed. | |
| 64 | |
| 65 // Parsing happens in three stages. In the first stage, we run the text | |
| 66 // against regular expressions that look for non-JSON patterns. We are | |
| 67 // especially concerned with '()' and 'new' because they can cause invocation, | |
| 68 // and '=' because it can cause mutation. But just to be safe, we want to | |
| 69 // reject all unexpected forms. | |
| 70 | |
| 71 // We split the first stage into 4 regexp operations in order to work around | |
| 72 // crippling inefficiencies in IE's and Safari's regexp engines. First we | |
| 73 // replace all backslash pairs with '@' (a non-JSON character). Second, we | |
| 74 // replace all simple value tokens with ']' characters. Third, we delete all | |
| 75 // open brackets that follow a colon or comma or that begin the text. Finally, | |
| 76 // we look to see that the remaining characters are only whitespace or ']' or | |
| 77 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. | |
| 78 | |
| 79 // Don't make these static since they have the global flag. | |
| 80 var backslashesRe = /\\["\\\/bfnrtu]/g; | |
| 81 var simpleValuesRe = | |
| 82 /"[^"\\\n\r\u2028\u2029\x00-\x1f\x7f-\x9f]*"|true|false|null|-?\d+(?:\.\d*
)?(?:[eE][+\-]?\d+)?/g; | |
| 83 var openBracketsRe = /(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g; | |
| 84 var remainderRe = /^[\],:{}\s\u2028\u2029]*$/; | |
| 85 | |
| 86 return remainderRe.test(s.replace(backslashesRe, '@'). | |
| 87 replace(simpleValuesRe, ']'). | |
| 88 replace(openBracketsRe, '')); | |
| 89 }; | |
| 90 | |
| 91 | |
| 92 /** | |
| 93 * Parses a JSON string and returns the result. This throws an exception if | |
| 94 * the string is an invalid JSON string. | |
| 95 * | |
| 96 * If the user agent has built in support for parsing JSON (using | |
| 97 * <code>String.prototype.parseJSON</code>) that will be used. | |
| 98 * | |
| 99 * Note that this is very slow on large strings. If you trust the source of | |
| 100 * the string then you should use unsafeParse instead. | |
| 101 * | |
| 102 * @param {string} s The JSON string to parse. | |
| 103 * @return {Object} The object generated from the JSON string. | |
| 104 */ | |
| 105 goog.json.parse = function(s) { | |
| 106 s = String(s); | |
| 107 if (typeof s.parseJSON == 'function') { | |
| 108 return s.parseJSON(); | |
| 109 } | |
| 110 if (goog.json.isValid_(s)) { | |
| 111 /** @preserveTry */ | |
| 112 try { | |
| 113 return goog.json.unsafeParse(s); | |
| 114 } catch (ex) { | |
| 115 } | |
| 116 } | |
| 117 throw Error('Invalid JSON string: ' + s); | |
| 118 }; | |
| 119 | |
| 120 | |
| 121 /** | |
| 122 * Parses a JSON string and returns the result. This uses eval so it is open | |
| 123 * to security issues and it should only be used if you trust the source. | |
| 124 * | |
| 125 * @param {string} s The JSON string to parse. | |
| 126 * @return {Object} The object generated from the JSON string. | |
| 127 */ | |
| 128 goog.json.unsafeParse = function(s) { | |
| 129 // This is lame. V8 disallows direct access to eval() in extensions (see: | |
| 130 // v8::internal::Parser::ParseLeftHandSideExpression()). So we must use this | |
| 131 // nasty hack instead. | |
| 132 return new Function('return ('+ s + ')')(); | |
| 133 }; | |
| 134 | |
| 135 | |
| 136 /** | |
| 137 * Instance of the serializer object. | |
| 138 * @type {goog.json.Serializer} | |
| 139 * @private | |
| 140 */ | |
| 141 goog.json.serializer_ = null; | |
| 142 | |
| 143 | |
| 144 /** | |
| 145 * Serializes an object or a value to a JSON string. | |
| 146 * | |
| 147 * If the user agent has built in support for serializing JSON (using | |
| 148 * <code>Object.prototype.toJSONString</code>) that will be used. | |
| 149 * | |
| 150 * @param {Object} object The object to serialize. | |
| 151 * @throws Error if there are loops in the object graph. | |
| 152 * @return {string} A JSON string representation of the input. | |
| 153 */ | |
| 154 goog.json.serialize = function(object) { | |
| 155 if (!goog.json.serializer_) { | |
| 156 goog.json.serializer_ = new goog.json.Serializer; | |
| 157 } | |
| 158 return goog.json.serializer_.serialize(object); | |
| 159 }; | |
| 160 | |
| 161 | |
| 162 | |
| 163 /** | |
| 164 * Class that is used to serialize JSON objects to a string. | |
| 165 * @constructor | |
| 166 */ | |
| 167 goog.json.Serializer = function() { | |
| 168 }; | |
| 169 | |
| 170 | |
| 171 /** | |
| 172 * Serializes an object or a value to a JSON string. | |
| 173 * | |
| 174 * If the user agent has built in support for serializing JSON (using | |
| 175 * <code>Object.prototype.toJSONString</code>) that will be used. | |
| 176 * | |
| 177 * @param {Object?} object The object to serialize. | |
| 178 * @throws Error if there are loops in the object graph. | |
| 179 * @return {string} A JSON string representation of the input. | |
| 180 */ | |
| 181 goog.json.Serializer.prototype.serialize = function(object) { | |
| 182 // null and undefined cannot have properties. (null == undefined) | |
| 183 if (object != null && typeof object.toJSONString == 'function') { | |
| 184 return object.toJSONString(); | |
| 185 } | |
| 186 var sb = []; | |
| 187 this.serialize_(object, sb); | |
| 188 return sb.join(''); | |
| 189 }; | |
| 190 | |
| 191 | |
| 192 /** | |
| 193 * Serializes a generic value to a JSON string | |
| 194 * @private | |
| 195 * @param {Object?} object The object to serialize. | |
| 196 * @param {Array} sb Array used as a string builder. | |
| 197 * @throws Error if there are loops in the object graph. | |
| 198 */ | |
| 199 goog.json.Serializer.prototype.serialize_ = function(object, sb) { | |
| 200 switch (typeof object) { | |
| 201 case 'string': | |
| 202 this.serializeString_(object, sb); | |
| 203 break; | |
| 204 case 'number': | |
| 205 this.serializeNumber_(object, sb); | |
| 206 break; | |
| 207 case 'boolean': | |
| 208 sb.push(object); | |
| 209 break; | |
| 210 case 'undefined': | |
| 211 sb.push('null'); | |
| 212 break; | |
| 213 case 'object': | |
| 214 if (object == null) { | |
| 215 sb.push('null'); | |
| 216 break; | |
| 217 } | |
| 218 if (goog.isArray(object)) { | |
| 219 this.serializeArray_(object, sb); | |
| 220 break; | |
| 221 } | |
| 222 // should we allow new String, new Number and new Boolean to be treated | |
| 223 // as string, number and boolean? Most implementations do not and the | |
| 224 // need is not very big | |
| 225 this.serializeObject_(object, sb); | |
| 226 break; | |
| 227 default: | |
| 228 throw Error('Unknown type: ' + typeof object); | |
| 229 } | |
| 230 }; | |
| 231 | |
| 232 | |
| 233 /** | |
| 234 * Character mappings used internally for goog.string.quote | |
| 235 * @private | |
| 236 * @type {Object} | |
| 237 */ | |
| 238 goog.json.Serializer.charToJsonCharCache_ = { | |
| 239 '\"': '\\"', | |
| 240 '\\': '\\\\', | |
| 241 '/': '\\/', | |
| 242 '\b': '\\b', | |
| 243 '\f': '\\f', | |
| 244 '\n': '\\n', | |
| 245 '\r': '\\r', | |
| 246 '\t': '\\t', | |
| 247 | |
| 248 '\x0B': '\\u000b' // '\v' is not supported in JScript | |
| 249 }; | |
| 250 | |
| 251 | |
| 252 /** | |
| 253 * Serializes a string to a JSON string | |
| 254 * @private | |
| 255 * @param {string} s The string to serialize. | |
| 256 * @param {Array} sb Array used as a string builder. | |
| 257 */ | |
| 258 goog.json.Serializer.prototype.serializeString_ = function(s, sb) { | |
| 259 // The official JSON implementation does not work with international | |
| 260 // characters. | |
| 261 sb.push('"', s.replace(/[\\\"\x00-\x1f\x80-\uffff]/g, function(c) { | |
| 262 // caching the result improves performance by a factor 2-3 | |
| 263 if (c in goog.json.Serializer.charToJsonCharCache_) { | |
| 264 return goog.json.Serializer.charToJsonCharCache_[c]; | |
| 265 } | |
| 266 | |
| 267 var cc = c.charCodeAt(0); | |
| 268 var rv = '\\u'; | |
| 269 if (cc < 16) { | |
| 270 rv += '000'; | |
| 271 } else if (cc < 256) { | |
| 272 rv += '00'; | |
| 273 } else if (cc < 4096) { // \u1000 | |
| 274 rv += '0'; | |
| 275 } | |
| 276 return goog.json.Serializer.charToJsonCharCache_[c] = rv + cc.toString(16); | |
| 277 }), '"'); | |
| 278 }; | |
| 279 | |
| 280 | |
| 281 /** | |
| 282 * Serializes a number to a JSON string | |
| 283 * @private | |
| 284 * @param {number} n The number to serialize. | |
| 285 * @param {Array} sb Array used as a string builder. | |
| 286 */ | |
| 287 goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) { | |
| 288 sb.push(isFinite(n) && !isNaN(n) ? n : 'null'); | |
| 289 }; | |
| 290 | |
| 291 | |
| 292 /** | |
| 293 * Serializes an array to a JSON string | |
| 294 * @private | |
| 295 * @param {Array} arr The array to serialize. | |
| 296 * @param {Array} sb Array used as a string builder. | |
| 297 */ | |
| 298 goog.json.Serializer.prototype.serializeArray_ = function(arr, sb) { | |
| 299 var l = arr.length; | |
| 300 sb.push('['); | |
| 301 var sep = ''; | |
| 302 for (var i = 0; i < l; i++) { | |
| 303 sb.push(sep) | |
| 304 this.serialize_(arr[i], sb); | |
| 305 sep = ','; | |
| 306 } | |
| 307 sb.push(']'); | |
| 308 }; | |
| 309 | |
| 310 | |
| 311 /** | |
| 312 * Serializes an object to a JSON string | |
| 313 * @private | |
| 314 * @param {Object} obj The object to serialize. | |
| 315 * @param {Array} sb Array used as a string builder. | |
| 316 */ | |
| 317 goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) { | |
| 318 sb.push('{'); | |
| 319 var sep = ''; | |
| 320 for (var key in obj) { | |
| 321 sb.push(sep); | |
| 322 this.serializeString_(key, sb); | |
| 323 sb.push(':'); | |
| 324 this.serialize_(obj[key], sb); | |
| 325 sep = ','; | |
| 326 } | |
| 327 sb.push('}'); | |
| 328 }; | |
| OLD | NEW |