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