Advertisement
Guest User

jsonRawDecode

a guest
Mar 26th, 2012
450
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // This source code is free for use in the public domain.
  2. // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  3.  
  4. // based on http://code.google.com/p/json-sans-eval/
  5. // (retrieved as of 21 Mar 2012)
  6.  
  7. /**
  8.  * Parses a first JSON object in a string, returning it and the index of
  9.  * the first character past the object.
  10.  *
  11.  * If there is only part of a JSON object, it will return [undefined, 0].
  12.  *
  13.  * If there is malformed JSON, the behavior is undefined. It may throw
  14.  * an Error, but it may just return [undefined, 0] or an incorrect
  15.  * object. However, it is deterministic and is guaranteed not to
  16.  * modify any object other than its return value.
  17.  *
  18.  * This does not use `eval` so is less likely to have obscure security bugs than
  19.  * json2.js.
  20.  * It is optimized for speed, so is much faster than json_parse.js.
  21.  *
  22.  * This library should be used whenever security is a concern (when JSON may
  23.  * come from an untrusted source), speed is a concern, and erroring on malformed
  24.  * JSON is *not* a concern.
  25.  *
  26.  *                      Pros                   Cons
  27.  *                    +-----------------------+-----------------------+
  28.  * json_sans_eval.js  | Fast, secure          | Not validating        |
  29.  *                    +-----------------------+-----------------------+
  30.  * json_parse.js      | Validating, secure    | Slow                  |
  31.  *                    +-----------------------+-----------------------+
  32.  * json2.js           | Fast, some validation | Potentially insecure  |
  33.  *                    +-----------------------+-----------------------+
  34.  *
  35.  * json2.js is very fast, but potentially insecure since it calls `eval` to
  36.  * parse JSON data, so an attacker might be able to supply strange JS that
  37.  * looks like JSON, but that executes arbitrary javascript.
  38.  * If you do have to use json2.js with untrusted data, make sure you keep
  39.  * your version of json2.js up to date so that you get patches as they're
  40.  * released.
  41.  *
  42.  * @param {string} 0 or more json text representations per RFC 4627;
  43.  *     the last may be incomplete
  44.  *
  45.  * @param {function (this:Object, string, *):*} opt_reviver optional function
  46.  *     that reworks JSON objects post-parse per Chapter 15.12 of EcmaScript3.1.
  47.  *     If supplied, the function is called with a string key, and a value.
  48.  *     The value is the property of 'this'.  The reviver should return
  49.  *     the value to use in its place.  So if dates were serialized as
  50.  *     {@code { "type": "Date", "time": 1234 }}, then a reviver might look like
  51.  *     {@code
  52.  *     function (key, value) {
  53.  *       if (value && typeof value === 'object' && 'Date' === value.type) {
  54.  *         return new Date(value.time);
  55.  *       } else {
  56.  *         return value;
  57.  *       }
  58.  *     }}.
  59.  *     If the reviver returns {@code undefined} then the property named by key
  60.  *     will be deleted from its container.
  61.  *     {@code this} is bound to the object containing the specified property.
  62.  *
  63.  * @return [{Object|Array|{@code undefined}}, Number]: The first object in the
  64.  *     stream, and the index of the first character beyond that object.
  65.  * @author Mike Samuel <mikesamuel@gmail.com>, Andrew Barnert <abarnert@yahoo.com>
  66.  */
  67. var jsonRawDecode = (function () {
  68.   var number
  69.       = '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)';
  70.   var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]'
  71.       + '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';
  72.   var string = '(?:\"' + oneChar + '*\")';
  73.  
  74.   // Will match a value in a well-formed JSON file.
  75.   // If the input is not well-formed, may match strangely, but not in an unsafe
  76.   // way.
  77.   // Since this only matches value tokens, it does not match whitespace, colons,
  78.   // or commas.
  79.   var jsonToken = new RegExp(
  80.       '(?:false|true|null|[\\{\\}\\[\\]]'
  81.       + '|' + number
  82.       + '|' + string
  83.       + ')', 'g');
  84.  
  85.   // Similar to the built-in match, but returns an array of match
  86.   // objects instead of an array of strings.
  87.   function tokenize(re, s) {
  88.     var matches = [];
  89.     while (true) {
  90.       var match = re.exec(s);
  91.       if (match == null) return matches;
  92.       matches.push(match);
  93.     }
  94.   }
  95.  
  96.   // Matches escape sequences in a string literal
  97.   var escapeSequence = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g');
  98.  
  99.   // Decodes escape sequences in object literals
  100.   var escapes = {
  101.     '"': '"',
  102.     '/': '/',
  103.     '\\': '\\',
  104.     'b': '\b',
  105.     'f': '\f',
  106.     'n': '\n',
  107.     'r': '\r',
  108.     't': '\t'
  109.   };
  110.   function unescapeOne(_, ch, hex) {
  111.     return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16));
  112.   }
  113.  
  114.   // A non-falsy value that coerces to the empty string when used as a key.
  115.   var EMPTY_STRING = new String('');
  116.   var SLASH = '\\';
  117.  
  118.   // Constructor to use based on an open token.
  119.   var firstTokenCtors = { '{': Object, '[': Array };
  120.  
  121.   var hop = Object.hasOwnProperty;
  122.  
  123.   return function (json, opt_reviver) {
  124.     // Split into tokens
  125.     var toks = tokenize(jsonToken, json);
  126.     if (!toks || !toks.length) { return [undefined, 0]; }
  127.     // Construct the object to return
  128.     var result;
  129.     var tok = toks[0].toString();
  130.     var topLevelPrimitive = false;
  131.     if ('{' === tok) {
  132.       result = {};
  133.     } else if ('[' === tok) {
  134.       result = [];
  135.     } else {
  136.       // The RFC only allows arrays or objects at the top level, but the JSON.parse
  137.       // defined by the EcmaScript 5 draft does allow strings, booleans, numbers, and null
  138.       // at the top level.
  139.       result = [];
  140.       topLevelPrimitive = true;
  141.     }
  142.    
  143.     var index = -1;
  144.  
  145.     // If undefined, the key in an object key/value record to use for the next
  146.     // value parsed.
  147.     var key;
  148.     // Loop over remaining tokens maintaining a stack of uncompleted objects and
  149.     // arrays.
  150.     var stack = [result];
  151.     for (var i = 1 - topLevelPrimitive, n = toks.length; i < n; ++i) {
  152.       tok = toks[i].toString();
  153.  
  154.       var cont;
  155.       switch (tok.toString().charCodeAt(0)) {
  156.         default:  // sign or digit
  157.           cont = stack[0];
  158.           if (cont == undefined) {
  159.             index = toks[i].index;
  160.             break;
  161.           }
  162.           cont[key || cont.length] = +(tok);
  163.           key = void 0;
  164.           break;
  165.         case 0x22:  // '"'
  166.           tok = tok.substring(1, tok.length - 1);
  167.           if (tok.indexOf(SLASH) !== -1) {
  168.             tok = tok.replace(escapeSequence, unescapeOne);
  169.           }
  170.           cont = stack[0];
  171.           if (cont == undefined) {
  172.             index = toks[i].index;
  173.             break;
  174.           }
  175.           if (!key) {
  176.             if (cont instanceof Array) {
  177.               key = cont.length;
  178.             } else {
  179.               key = tok || EMPTY_STRING;  // Use as key for next value seen.
  180.               break;
  181.             }
  182.           }
  183.           cont[key] = tok;
  184.           key = void 0;
  185.           break;
  186.         case 0x5b:  // '['
  187.           cont = stack[0];
  188.           if (cont == undefined) {
  189.             index = toks[i].index;
  190.             break;
  191.           }
  192.           stack.unshift(cont[key || cont.length] = []);
  193.           key = void 0;
  194.           break;
  195.         case 0x5d:  // ']'
  196.           stack.shift();
  197.           break;
  198.         case 0x66:  // 'f'
  199.           cont = stack[0];
  200.           if (cont == undefined) {
  201.             index = toks[i].index;
  202.             break;
  203.           }
  204.           cont[key || cont.length] = false;
  205.           key = void 0;
  206.           break;
  207.         case 0x6e:  // 'n'
  208.           cont = stack[0];
  209.           if (cont == undefined) {
  210.             index = toks[i].index;
  211.             break;
  212.           }
  213.           cont[key || cont.length] = null;
  214.           key = void 0;
  215.           break;
  216.         case 0x74:  // 't'
  217.           cont = stack[0];
  218.           if (cont == undefined) {
  219.             index = toks[i].index;
  220.             break;
  221.           }
  222.           cont[key || cont.length] = true;
  223.           key = void 0;
  224.           break;
  225.         case 0x7b:  // '{'
  226.           cont = stack[0];
  227.           if (cont == undefined) {
  228.             index = toks[i].index;
  229.             break;
  230.           }
  231.           stack.unshift(cont[key || cont.length] = {});
  232.           key = void 0;
  233.           break;
  234.         case 0x7d:  // '}'
  235.           stack.shift();
  236.           break;
  237.       }
  238.       if (index >= 0) break;
  239.     }
  240.     // Fail if we've got an uncompleted object.
  241.     if (topLevelPrimitive) {
  242.       if (stack.length !== 1) {
  243.     return(undefined, 0); // Incomplete top-level primitive
  244.       }
  245.       result = result[0];
  246.     } else {
  247.       if (stack.length) {
  248.     return(undefined, 0); // Incomplete top-level obj/array
  249.       }
  250.     }
  251.  
  252.     if (opt_reviver) {
  253.       // Based on walk as implemented in http://www.json.org/json2.js
  254.       var walk = function (holder, key) {
  255.         var value = holder[key];
  256.         if (value && typeof value === 'object') {
  257.           var toDelete = null;
  258.           for (var k in value) {
  259.             if (hop.call(value, k) && value !== holder) {
  260.               // Recurse to properties first.  This has the effect of causing
  261.               // the reviver to be called on the object graph depth-first.
  262.  
  263.               // Since 'this' is bound to the holder of the property, the
  264.               // reviver can access sibling properties of k including ones
  265.               // that have not yet been revived.
  266.  
  267.               // The value returned by the reviver is used in place of the
  268.               // current value of property k.
  269.               // If it returns undefined then the property is deleted.
  270.               var v = walk(value, k);
  271.               if (v !== void 0) {
  272.                 value[k] = v;
  273.               } else {
  274.                 // Deleting properties inside the loop has vaguely defined
  275.                 // semantics in ES3 and ES3.1.
  276.                 if (!toDelete) { toDelete = []; }
  277.                 toDelete.push(k);
  278.               }
  279.             }
  280.           }
  281.           if (toDelete) {
  282.             for (var i = toDelete.length; --i >= 0;) {
  283.               delete value[toDelete[i]];
  284.             }
  285.           }
  286.         }
  287.         return opt_reviver.call(holder, key, value);
  288.       };
  289.       result = walk({ '': result }, '');
  290.     }
  291.  
  292.     if (index == -1) index = json.length;
  293.     return [result, index];
  294.   };
  295. })();
  296.  
  297. /*
  298. j0 = '{a:]}'
  299. j05 = '{"id":1,"result":{"answer'
  300. j1 = '{"id":1,"result":{"answer":23},"error":null}'
  301. j15 = '{"id":1,"result":{"answer":23},"error":null}{"id":2,"result":{"answer';
  302. j2 = '{"id":1,"result":{"answer":23},"error":null}{"id":2,"result":{"answer":42},"error":null}';
  303. print(JSON.stringify(jsonRawDecode(j2)))
  304. print(JSON.stringify(jsonRawDecode(j15)))
  305. print(JSON.stringify(jsonRawDecode(j1)))
  306. print(JSON.stringify(jsonRawDecode(j05)))
  307. print(JSON.stringify(jsonRawDecode(j0)))
  308. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement