From dc74340733f187dc3d562079b55d5667759a9d41 Mon Sep 17 00:00:00 2001 From: Georg Hopp Date: Thu, 11 Oct 2007 14:15:13 +0000 Subject: [PATCH] --- TODO | 1 + json.js | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 json.js diff --git a/TODO b/TODO index 6771779..de3bfe6 100644 --- a/TODO +++ b/TODO @@ -5,3 +5,4 @@ Siehe: http://www.admin-wissen.de/tutorials/eigene-tutorials/webentwicklung/ajax-tutorial/ http://www.json.org/ + http://yuiblog.com/blog/2006/09/26/for-in-intrigue/ diff --git a/json.js b/json.js new file mode 100644 index 0000000..978f2b4 --- /dev/null +++ b/json.js @@ -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); +} \ No newline at end of file