Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/json/json.js
2868 views
1
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview JSON utility functions.
17
* @author [email protected] (Erik Arvidsson)
18
*/
19
20
21
goog.provide('goog.json');
22
goog.provide('goog.json.Replacer');
23
goog.provide('goog.json.Reviver');
24
goog.provide('goog.json.Serializer');
25
26
27
/**
28
* @define {boolean} If true, use the native JSON parsing API.
29
* NOTE: The default {@code goog.json.parse} implementation is able to handle
30
* invalid JSON. JSPB used to produce invalid JSON which is not the case
31
* anymore so this is safe to enable for parsing JSPB. Using native JSON is
32
* faster and safer than the default implementation using {@code eval}.
33
*/
34
goog.define('goog.json.USE_NATIVE_JSON', false);
35
36
/**
37
* @define {boolean} If true, try the native JSON parsing API first. If it
38
* fails, log an error and use {@code eval} instead. This is useful when
39
* transitioning to {@code goog.json.USE_NATIVE_JSON}. The error logger needs to
40
* be set by {@code goog.json.setErrorLogger}. If it is not set then the error
41
* is ignored.
42
*/
43
goog.define('goog.json.TRY_NATIVE_JSON', false);
44
45
46
/**
47
* Tests if a string is an invalid JSON string. This only ensures that we are
48
* not using any invalid characters
49
* @param {string} s The string to test.
50
* @return {boolean} True if the input is a valid JSON string.
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
// We allowed \x7f inside strings because some tools don't escape it,
65
// e.g. http://www.json.org/java/org/json/JSONObject.java
66
67
// Parsing happens in three stages. In the first stage, we run the text
68
// against regular expressions that look for non-JSON patterns. We are
69
// especially concerned with '()' and 'new' because they can cause invocation,
70
// and '=' because it can cause mutation. But just to be safe, we want to
71
// reject all unexpected forms.
72
73
// We split the first stage into 4 regexp operations in order to work around
74
// crippling inefficiencies in IE's and Safari's regexp engines. First we
75
// replace all backslash pairs with '@' (a non-JSON character). Second, we
76
// replace all simple value tokens with ']' characters, but only when followed
77
// by a colon, comma, closing bracket or end of string. Third, we delete all
78
// open brackets that follow a colon or comma or that begin the text. Finally,
79
// we look to see that the remaining characters are only whitespace or ']' or
80
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
81
82
// Don't make these static since they have the global flag.
83
var backslashesRe = /\\["\\\/bfnrtu]/g;
84
var simpleValuesRe =
85
/(?:"[^"\\\n\r\u2028\u2029\x00-\x08\x0a-\x1f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)[\s\u2028\u2029]*(?=:|,|]|}|$)/g;
86
var openBracketsRe = /(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g;
87
var remainderRe = /^[\],:{}\s\u2028\u2029]*$/;
88
89
return remainderRe.test(
90
s.replace(backslashesRe, '@')
91
.replace(simpleValuesRe, ']')
92
.replace(openBracketsRe, ''));
93
};
94
95
/**
96
* Logs a parsing error in {@code JSON.parse} solvable by using {@code eval}
97
* if {@code goog.json.TRY_NATIVE_JSON} is enabled.
98
* @private {function(string, !Error)} The first parameter is the error message,
99
* the second is the exception thrown by {@code JSON.parse}.
100
*/
101
goog.json.errorLogger_ = goog.nullFunction;
102
103
104
/**
105
* Sets an error logger to use if there's a recoverable parsing error and {@code
106
* goog.json.TRY_NATIVE_JSON} is enabled.
107
* @param {function(string, !Error)} errorLogger The first parameter is the
108
* error message, the second is the exception thrown by {@code JSON.parse}.
109
*/
110
goog.json.setErrorLogger = function(errorLogger) {
111
goog.json.errorLogger_ = errorLogger;
112
};
113
114
115
/**
116
* Parses a JSON string and returns the result. This throws an exception if
117
* the string is an invalid JSON string.
118
*
119
* Note that this is very slow on large strings. If you trust the source of
120
* the string then you should use unsafeParse instead.
121
*
122
* @param {*} s The JSON string to parse.
123
* @throws Error if s is invalid JSON.
124
* @return {Object} The object generated from the JSON string, or null.
125
*/
126
goog.json.parse = goog.json.USE_NATIVE_JSON ?
127
/** @type {function(*):Object} */ (goog.global['JSON']['parse']) :
128
function(s) {
129
var error;
130
if (goog.json.TRY_NATIVE_JSON) {
131
try {
132
return goog.global['JSON']['parse'](s);
133
} catch (ex) {
134
error = ex;
135
}
136
}
137
var o = String(s);
138
if (goog.json.isValid(o)) {
139
140
try {
141
var result = /** @type {?Object} */ (eval('(' + o + ')'));
142
if (error) {
143
goog.json.errorLogger_('Invalid JSON: ' + o, error);
144
}
145
return result;
146
} catch (ex) {
147
}
148
}
149
throw Error('Invalid JSON string: ' + o);
150
};
151
152
153
/**
154
* Parses a JSON string and returns the result. This uses eval so it is open
155
* to security issues and it should only be used if you trust the source.
156
*
157
* @param {string} s The JSON string to parse.
158
* @return {Object} The object generated from the JSON string.
159
* @deprecated Use JSON.parse if possible or goog.json.parse.
160
*/
161
goog.json.unsafeParse = goog.json.USE_NATIVE_JSON ?
162
/** @type {function(string):Object} */ (goog.global['JSON']['parse']) :
163
function(s) {
164
var error;
165
if (goog.json.TRY_NATIVE_JSON) {
166
try {
167
return goog.global['JSON']['parse'](s);
168
} catch (ex) {
169
error = ex;
170
}
171
}
172
var result = /** @type {?Object} */ (eval('(' + s + ')'));
173
if (error) {
174
goog.json.errorLogger_('Invalid JSON: ' + s, error);
175
}
176
return result;
177
};
178
179
180
/**
181
* JSON replacer, as defined in Section 15.12.3 of the ES5 spec.
182
* @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3
183
*
184
* TODO(nicksantos): Array should also be a valid replacer.
185
*
186
* @typedef {function(this:Object, string, *): *}
187
*/
188
goog.json.Replacer;
189
190
191
/**
192
* JSON reviver, as defined in Section 15.12.2 of the ES5 spec.
193
* @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3
194
*
195
* @typedef {function(this:Object, string, *): *}
196
*/
197
goog.json.Reviver;
198
199
200
/**
201
* Serializes an object or a value to a JSON string.
202
*
203
* @param {*} object The object to serialize.
204
* @param {?goog.json.Replacer=} opt_replacer A replacer function
205
* called for each (key, value) pair that determines how the value
206
* should be serialized. By defult, this just returns the value
207
* and allows default serialization to kick in.
208
* @throws Error if there are loops in the object graph.
209
* @return {string} A JSON string representation of the input.
210
*/
211
goog.json.serialize = goog.json.USE_NATIVE_JSON ?
212
/** @type {function(*, ?goog.json.Replacer=):string} */
213
(goog.global['JSON']['stringify']) :
214
function(object, opt_replacer) {
215
// NOTE(nicksantos): Currently, we never use JSON.stringify.
216
//
217
// The last time I evaluated this, JSON.stringify had subtle bugs and
218
// behavior differences on all browsers, and the performance win was not
219
// large enough to justify all the issues. This may change in the future
220
// as browser implementations get better.
221
//
222
// assertSerialize in json_test contains if branches for the cases
223
// that fail.
224
return new goog.json.Serializer(opt_replacer).serialize(object);
225
};
226
227
228
229
/**
230
* Class that is used to serialize JSON objects to a string.
231
* @param {?goog.json.Replacer=} opt_replacer Replacer.
232
* @constructor
233
*/
234
goog.json.Serializer = function(opt_replacer) {
235
/**
236
* @type {goog.json.Replacer|null|undefined}
237
* @private
238
*/
239
this.replacer_ = opt_replacer;
240
};
241
242
243
/**
244
* Serializes an object or a value to a JSON string.
245
*
246
* @param {*} object The object to serialize.
247
* @throws Error if there are loops in the object graph.
248
* @return {string} A JSON string representation of the input.
249
*/
250
goog.json.Serializer.prototype.serialize = function(object) {
251
var sb = [];
252
this.serializeInternal(object, sb);
253
return sb.join('');
254
};
255
256
257
/**
258
* Serializes a generic value to a JSON string
259
* @protected
260
* @param {*} object The object to serialize.
261
* @param {Array<string>} sb Array used as a string builder.
262
* @throws Error if there are loops in the object graph.
263
*/
264
goog.json.Serializer.prototype.serializeInternal = function(object, sb) {
265
if (object == null) {
266
// undefined == null so this branch covers undefined as well as null
267
sb.push('null');
268
return;
269
}
270
271
if (typeof object == 'object') {
272
if (goog.isArray(object)) {
273
this.serializeArray(object, sb);
274
return;
275
} else if (
276
object instanceof String || object instanceof Number ||
277
object instanceof Boolean) {
278
object = object.valueOf();
279
// Fall through to switch below.
280
} else {
281
this.serializeObject_(/** @type {!Object} */ (object), sb);
282
return;
283
}
284
}
285
286
switch (typeof object) {
287
case 'string':
288
this.serializeString_(object, sb);
289
break;
290
case 'number':
291
this.serializeNumber_(object, sb);
292
break;
293
case 'boolean':
294
sb.push(String(object));
295
break;
296
case 'function':
297
sb.push('null');
298
break;
299
default:
300
throw Error('Unknown type: ' + typeof object);
301
}
302
};
303
304
305
/**
306
* Character mappings used internally for goog.string.quote
307
* @private
308
* @type {!Object}
309
*/
310
goog.json.Serializer.charToJsonCharCache_ = {
311
'\"': '\\"',
312
'\\': '\\\\',
313
'/': '\\/',
314
'\b': '\\b',
315
'\f': '\\f',
316
'\n': '\\n',
317
'\r': '\\r',
318
'\t': '\\t',
319
320
'\x0B': '\\u000b' // '\v' is not supported in JScript
321
};
322
323
324
/**
325
* Regular expression used to match characters that need to be replaced.
326
* The S60 browser has a bug where unicode characters are not matched by
327
* regular expressions. The condition below detects such behaviour and
328
* adjusts the regular expression accordingly.
329
* @private
330
* @type {!RegExp}
331
*/
332
goog.json.Serializer.charsToReplace_ = /\uffff/.test('\uffff') ?
333
/[\\\"\x00-\x1f\x7f-\uffff]/g :
334
/[\\\"\x00-\x1f\x7f-\xff]/g;
335
336
337
/**
338
* Serializes a string to a JSON string
339
* @private
340
* @param {string} s The string to serialize.
341
* @param {Array<string>} sb Array used as a string builder.
342
*/
343
goog.json.Serializer.prototype.serializeString_ = function(s, sb) {
344
// The official JSON implementation does not work with international
345
// characters.
346
sb.push('"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {
347
// caching the result improves performance by a factor 2-3
348
var rv = goog.json.Serializer.charToJsonCharCache_[c];
349
if (!rv) {
350
rv = '\\u' + (c.charCodeAt(0) | 0x10000).toString(16).substr(1);
351
goog.json.Serializer.charToJsonCharCache_[c] = rv;
352
}
353
return rv;
354
}), '"');
355
};
356
357
358
/**
359
* Serializes a number to a JSON string
360
* @private
361
* @param {number} n The number to serialize.
362
* @param {Array<string>} sb Array used as a string builder.
363
*/
364
goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {
365
sb.push(isFinite(n) && !isNaN(n) ? String(n) : 'null');
366
};
367
368
369
/**
370
* Serializes an array to a JSON string
371
* @param {Array<string>} arr The array to serialize.
372
* @param {Array<string>} sb Array used as a string builder.
373
* @protected
374
*/
375
goog.json.Serializer.prototype.serializeArray = function(arr, sb) {
376
var l = arr.length;
377
sb.push('[');
378
var sep = '';
379
for (var i = 0; i < l; i++) {
380
sb.push(sep);
381
382
var value = arr[i];
383
this.serializeInternal(
384
this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,
385
sb);
386
387
sep = ',';
388
}
389
sb.push(']');
390
};
391
392
393
/**
394
* Serializes an object to a JSON string
395
* @private
396
* @param {!Object} obj The object to serialize.
397
* @param {Array<string>} sb Array used as a string builder.
398
*/
399
goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {
400
sb.push('{');
401
var sep = '';
402
for (var key in obj) {
403
if (Object.prototype.hasOwnProperty.call(obj, key)) {
404
var value = obj[key];
405
// Skip functions.
406
if (typeof value != 'function') {
407
sb.push(sep);
408
this.serializeString_(key, sb);
409
sb.push(':');
410
411
this.serializeInternal(
412
this.replacer_ ? this.replacer_.call(obj, key, value) : value, sb);
413
414
sep = ',';
415
}
416
}
417
}
418
sb.push('}');
419
};
420
421