2 changed files with 320 additions and 0 deletions
@ -0,0 +1,319 @@ |
|||||
|
/* |
||||
|
json.js |
||||
|
2007-10-10 |
||||
|
|
||||
|
Public Domain |
||||
|
|
||||
|
This file adds these methods to JavaScript: |
||||
|
|
||||
|
array.toJSONString(whitelist) |
||||
|
boolean.toJSONString() |
||||
|
date.toJSONString() |
||||
|
number.toJSONString() |
||||
|
object.toJSONString(whitelist) |
||||
|
string.toJSONString() |
||||
|
These methods produce a JSON text from a JavaScript value. |
||||
|
It must not contain any cyclical references. Illegal values |
||||
|
will be excluded. |
||||
|
|
||||
|
The default conversion for dates is to an ISO string. You can |
||||
|
add a toJSONString method to any date object to get a different |
||||
|
representation. |
||||
|
|
||||
|
The object and array methods can take an optional whitelist |
||||
|
argument. A whitelist is an array of strings. If it is provided, |
||||
|
keys in objects not found in the whitelist are excluded. |
||||
|
|
||||
|
string.parseJSON(filter) |
||||
|
This method parses a JSON text to produce an object or |
||||
|
array. It can throw a SyntaxError exception. |
||||
|
|
||||
|
The optional filter parameter is a function which can filter and |
||||
|
transform the results. It receives each of the keys and values, and |
||||
|
its return value is used instead of the original value. If it |
||||
|
returns what it received, then structure is not modified. If it |
||||
|
returns undefined then the member is deleted. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
// Parse the text. If a key contains the string 'date' then
|
||||
|
// convert the value to a date.
|
||||
|
|
||||
|
myData = text.parseJSON(function (key, value) { |
||||
|
return key.indexOf('date') >= 0 ? new Date(value) : value; |
||||
|
}); |
||||
|
|
||||
|
It is expected that these methods will formally become part of the |
||||
|
JavaScript Programming Language in the Fourth Edition of the |
||||
|
ECMAScript standard in 2008. |
||||
|
|
||||
|
This file will break programs with improper for..in loops. See |
||||
|
http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
|
||||
|
|
||||
|
This is a reference implementation. You are free to copy, modify, or |
||||
|
redistribute. |
||||
|
|
||||
|
Use your own copy. It is extremely unwise to load untrusted third party |
||||
|
code into your pages. |
||||
|
*/ |
||||
|
|
||||
|
/*jslint evil: true */ |
||||
|
|
||||
|
// Augment the basic prototypes if they have not already been augmented.
|
||||
|
|
||||
|
if (!Object.prototype.toJSONString) { |
||||
|
|
||||
|
Array.prototype.toJSONString = function (w) { |
||||
|
var a = [], // The array holding the partial texts.
|
||||
|
i, // Loop counter.
|
||||
|
l = this.length, |
||||
|
v; // The value to be stringified.
|
||||
|
|
||||
|
// For each value in this array...
|
||||
|
|
||||
|
for (i = 0; i < l; i += 1) { |
||||
|
v = this[i]; |
||||
|
switch (typeof v) { |
||||
|
case 'object': |
||||
|
|
||||
|
// Serialize a JavaScript object value. Treat objects thats lack the
|
||||
|
// toJSONString method as null. Due to a specification error in ECMAScript,
|
||||
|
// typeof null is 'object', so watch out for that case.
|
||||
|
|
||||
|
if (v && typeof v.toJSONString === 'function') { |
||||
|
a.push(v.toJSONString(w)); |
||||
|
} else { |
||||
|
a.push('null'); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case 'string': |
||||
|
case 'number': |
||||
|
case 'boolean': |
||||
|
a.push(v.toJSONString()); |
||||
|
break; |
||||
|
default: |
||||
|
a.push('null'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Join all of the member texts together and wrap them in brackets.
|
||||
|
|
||||
|
return '[' + a.join(',') + ']'; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Boolean.prototype.toJSONString = function () { |
||||
|
return String(this); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Date.prototype.toJSONString = function () { |
||||
|
|
||||
|
// Eventually, this method will be based on the date.toISOString method.
|
||||
|
|
||||
|
function f(n) { |
||||
|
|
||||
|
// Format integers to have at least two digits.
|
||||
|
|
||||
|
return n < 10 ? '0' + n : n; |
||||
|
} |
||||
|
|
||||
|
return '"' + this.getUTCFullYear() + '-' + |
||||
|
f(this.getUTCMonth() + 1) + '-' + |
||||
|
f(this.getUTCDate()) + 'T' + |
||||
|
f(this.getUTCHours()) + ':' + |
||||
|
f(this.getUTCMinutes()) + ':' + |
||||
|
f(this.getUTCSeconds()) + 'Z"'; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Number.prototype.toJSONString = function () { |
||||
|
|
||||
|
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
|
||||
|
return isFinite(this) ? String(this) : 'null'; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Object.prototype.toJSONString = function (w) { |
||||
|
var a = [], // The array holding the partial texts.
|
||||
|
k, // The current key.
|
||||
|
i, // The loop counter.
|
||||
|
v; // The current value.
|
||||
|
|
||||
|
// If a whitelist (array of keys) is provided, use it assemble the components
|
||||
|
// of the object.
|
||||
|
|
||||
|
if (w) { |
||||
|
for (i = 0; i < w.length; i += 1) { |
||||
|
k = w[i]; |
||||
|
if (typeof k === 'string') { |
||||
|
v = this[k]; |
||||
|
switch (typeof v) { |
||||
|
case 'object': |
||||
|
|
||||
|
// Serialize a JavaScript object value. Ignore objects that lack the
|
||||
|
// toJSONString method. Due to a specification error in ECMAScript,
|
||||
|
// typeof null is 'object', so watch out for that case.
|
||||
|
|
||||
|
if (v) { |
||||
|
if (typeof v.toJSONString === 'function') { |
||||
|
a.push(k.toJSONString() + ':' + |
||||
|
v.toJSONString(w)); |
||||
|
} |
||||
|
} else { |
||||
|
a.push(k.toJSONString() + ':null'); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case 'string': |
||||
|
case 'number': |
||||
|
case 'boolean': |
||||
|
a.push(k.toJSONString() + ':' + v.toJSONString()); |
||||
|
|
||||
|
// Values without a JSON representation are ignored.
|
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
|
||||
|
// Iterate through all of the keys in the object, ignoring the proto chain
|
||||
|
// and keys that are not strings.
|
||||
|
|
||||
|
for (k in this) { |
||||
|
if (typeof k === 'string' && |
||||
|
Object.prototype.hasOwnProperty.apply(this, [k])) { |
||||
|
v = this[k]; |
||||
|
switch (typeof v) { |
||||
|
case 'object': |
||||
|
|
||||
|
// Serialize a JavaScript object value. Ignore objects that lack the
|
||||
|
// toJSONString method. Due to a specification error in ECMAScript,
|
||||
|
// typeof null is 'object', so watch out for that case.
|
||||
|
|
||||
|
if (v) { |
||||
|
if (typeof v.toJSONString === 'function') { |
||||
|
a.push(k.toJSONString() + ':' + |
||||
|
v.toJSONString()); |
||||
|
} |
||||
|
} else { |
||||
|
a.push(k.toJSONString() + ':null'); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case 'string': |
||||
|
case 'number': |
||||
|
case 'boolean': |
||||
|
a.push(k.toJSONString() + ':' + v.toJSONString()); |
||||
|
|
||||
|
// Values without a JSON representation are ignored.
|
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Join all of the member texts together and wrap them in braces.
|
||||
|
|
||||
|
return '{' + a.join(',') + '}'; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
(function (s) { |
||||
|
|
||||
|
// Augment String.prototype. We do this in an immediate anonymous function to
|
||||
|
// avoid defining global variables.
|
||||
|
|
||||
|
// m is a table of character substitutions.
|
||||
|
|
||||
|
var m = { |
||||
|
'\b': '\\b', |
||||
|
'\t': '\\t', |
||||
|
'\n': '\\n', |
||||
|
'\f': '\\f', |
||||
|
'\r': '\\r', |
||||
|
'"' : '\\"', |
||||
|
'\\': '\\\\' |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
s.parseJSON = function (filter) { |
||||
|
var j; |
||||
|
|
||||
|
function walk(k, v) { |
||||
|
var i, n; |
||||
|
if (v && typeof v === 'object') { |
||||
|
for (i in v) { |
||||
|
if (Object.prototype.hasOwnProperty.apply(v, [i])) { |
||||
|
n = walk(i, v[i]); |
||||
|
if (n !== undefined) { |
||||
|
v[i] = n; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return filter(k, v); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Parsing happens in three stages. In the first stage, we run the text against
|
||||
|
// a regular expression which looks for non-JSON characters. We are especially
|
||||
|
// concerned with '()' and 'new' because they can cause invocation, and '='
|
||||
|
// because it can cause mutation. But just to be safe, we will reject all
|
||||
|
// unexpected characters.
|
||||
|
|
||||
|
// We split the first stage into 4 regexp operations in order to work around
|
||||
|
// crippling deficiencies in IE's and Safari's regexp engines. First we replace
|
||||
|
// all backslash pairs with '@' (a non-JSON character). Second, we replace all
|
||||
|
// simple value tokens with ']' characters. Third, we delete all open brackets
|
||||
|
// that follow a colon or comma or that begin the text. Finally, we look to see
|
||||
|
// that the remaining characters are only whitespace or ']' or ',' or ':' or '{'
|
||||
|
// or '}'. If that is so, then the text is safe for eval.
|
||||
|
|
||||
|
if (/^[\],:{}\s]*$/.test(this.replace(/\\./g, '@'). |
||||
|
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']'). |
||||
|
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
||||
|
|
||||
|
// In the second stage we use the eval function to compile the text into a
|
||||
|
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||
|
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
|
// in parens to eliminate the ambiguity.
|
||||
|
|
||||
|
j = eval('(' + this + ')'); |
||||
|
|
||||
|
// In the optional third stage, we recursively walk the new structure, passing
|
||||
|
// each name/value pair to a filter function for possible transformation.
|
||||
|
|
||||
|
return typeof filter === 'function' ? walk('', j) : j; |
||||
|
} |
||||
|
|
||||
|
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||
|
|
||||
|
throw new SyntaxError('parseJSON'); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
s.toJSONString = function () { |
||||
|
|
||||
|
// If the string contains no control characters, no quote characters, and no
|
||||
|
// backslash characters, then we can simply slap some quotes around it.
|
||||
|
// Otherwise we must also replace the offending characters with safe
|
||||
|
// sequences.
|
||||
|
|
||||
|
if (/["\\\x00-\x1f]/.test(this)) { |
||||
|
return '"' + this.replace(/[\x00-\x1f\\"]/g, function (a) { |
||||
|
var c = m[a]; |
||||
|
if (c) { |
||||
|
return c; |
||||
|
} |
||||
|
c = a.charCodeAt(); |
||||
|
return '\\u00' + Math.floor(c / 16).toString(16) + |
||||
|
(c % 16).toString(16); |
||||
|
}) + '"'; |
||||
|
} |
||||
|
return '"' + this + '"'; |
||||
|
}; |
||||
|
})(String.prototype); |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue