JavaScript json_decode
Decodes the JSON representation into a PHP value
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 | function json_decode (str_json) { // Decodes the JSON representation into a PHP value // // version: 1008.1718 // discuss at: http://phpjs.org/functions/json_decode // + original by: Public Domain (http://www.json.org/json2.js) // + reimplemented by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + improved by: T.J. Leahy // + improved by: Michael White // * example 1: json_decode('[\n "e",\n {\n "pluribus": "unum"\n}\n]'); // * returns 1: ['e', {pluribus: 'unum'}] /* http://www.JSON.org/json2.js 2008-11-19 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html */ var json = this.window.JSON; if (typeof json === 'object' && typeof json.parse === 'function') { try { return json.parse(str_json); } catch(err) { if (!(err instanceof SyntaxError)) { throw new Error('Unexpected error type in json_decode()'); } this.php_js = this.php_js || {}; this.php_js.last_error_json = 4; // usable by json_last_error() return null; } } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; var j; var text = str_json; // Parsing happens in four stages. In the first stage, we replace certain // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } // In the second stage, we run the text against regular expressions that look // for non-JSON patterns. We are especially concerned with '()' and 'new' // because they can cause invocation, and '=' because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we // replace the JSON 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(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { // In the third 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('(' + text + ')'); return j; } this.php_js = this.php_js || {}; this.php_js.last_error_json = 4; // usable by json_last_error() return null; } |
Examples
Running
1 | json_decode('[\n "e",\n {\n "pluribus": "unum"\n}\n]'); |
Should return
1 | ['e', {pluribus: 'unum'}] |
Dependencies
No dependencies, you can use this function standalone.
Open syntax issues
php.js uses JsLint to help us keep our code consistent and prevent some common bugs.
Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.
Authors
Thanks to the following developers, you get to have json_decode goodness in JavaScript.
@ Michael White: Just now saw your comment on json_encode & implemented the fixes!
http://github.com/kvz/phpjs/commit/cae72555c08c11ec416f1c8ecfcd5e42509cb46d
Yeah we need to look into this. Could be much better but I don't have enough time to work it myself right now. Maybe someone else reading this can have a go at it?
Would be much appreciated!
@kevin Hey - sorry. that's my fault. It should indeed return NULL if there's an error. My main point was that we should use the try/catch to prevent exceptions from causing the JS to just stop unexpectedly.
@Michael: Thanks, but is that really how PHP handles it? the docs for json_decode say "NULL is returned if the json cannot be decoded or if the encoded data is deeper than the recursion limit." Would be nice if someone could check for all possible json.parse/json.stringify errors (and the rest of the function when these are not available) to check for the type of errors also recorded by json_last_error (and to add a property to the "this.php_js" global so that json_last_error() can retrieve this info). Ditto for json_encode().
The part that uses native browser JSON objects should be contained in a try/catch block and return the proper value in accordance with how PHP handles bad JSON data.
try {
return json.parse(str_json);
}
catch(err) {
return str_json;
}
Fixed in SVN. Thanks for the report!
FYI, if you reference a variable like JSON and it doesn't exist, it will cause a failure in JS, so we have to either enclose such a reference in a try-catch block (ugly) or reference it as a property of the window object--since referencing an undefined property does not give an error in JS.
As far as why I used "this.window" ("window" would be fine for most environments), it is because this will both refer to:
1) the window object in the non-namespaced version (it resolves to "window.window" which, in JS, thankfully for our needs, happens to be a recursive reference to window), and
2) the window attached to the object itself in the namespaced version (our namespaced version allows for the window to be set to a value other than the global "window" object for environments like JavaScript modules in Firefox extensions where 'window' is not available as a global but is passed in during object construction, and should automatically set it to the window global otherwise).
This function should check to see if the browser has native JSON decoding first (IE8, FF 3.5) and use that when available. Would make it faster and safer then running the input against multiple regular expressions. See http://hacks.mozilla.org/2009/06/security-performance-native-json/
if (typeof JSON == "object" && typeof JSON.parse == "function") {
return JSON.parse(str_json);
}


Brett Zamir
26 Oct '09