2010-08-24 34 views
16

我有一个非常大的JSON字符串,我需要使用浏览器中的JavaScript解析。现在,在几个浏览器中,我用完了堆栈空间。不幸的是,我的JSON可以包含用户字符串,所以我不能使用eval或者让浏览器解析它。非递归JavaScript JSON解析器

我看过一些标准的JavaScript JSON解析器,它们是递归的。想知道是否有人知道任何安全和非递归的JSON解析器。我愿意为它提供更少的功能 - 我只有一大堆对象。

另外,如果有人知道一个可能很容易修改的东西,那也是一个很大的帮助。

编辑:仔细检查堆栈溢出是由分析器内使用的eval()抛出。所以,它必须是递归的。

+0

那么,递归编码与否,解析一个深度嵌套的结构必须经过相同的过程。你确定这个结构真的有效吗? “非常大”有多大? – Pointy 2010-08-24 14:44:21

+0

这是合法的 - 它是一个包含几千个对象的数组。当它达到一定的大小时,eval()不能解析它(IE可以,但其他浏览器不能) – 2010-08-24 14:54:31

+0

@Pointy yes它会经历相同的过程,但它不需要使用堆栈可能会耗尽空间。据推测,一个非递归的在堆中建立了一些其他等效的数据结构(希望它更大) – 2010-08-24 14:55:51

回答

0

在浏览器中的JSON解析通常只用eval来完成,但是在正则表达式“lint”之前的eval中,应该使得评估JSON变得安全。

有在维基百科上这样的例子:

+1

我现在看到eval在检查安全性后被调用,然后它引发堆栈溢出(因为它是内部递归的,JSON字符串很大) – 2010-08-24 14:40:17

+0

谢谢 - 你的建议帮助我看到真正的问题出现在eval()中。 – 2010-08-24 14:52:51

1

我建议你把JSON字符串分成几块,然后按需提供。也可能使用AJAX,你可以有一个适合你的需求的食谱。 使用“分而治之”的机制,我认为你仍然可以使用常见的JSON解析方法。

希望帮助,

+0

我们认为,但考虑到其他制约因素很困难。我们需要一次发送回复。分隔它以便split()会起作用需要另一次转义。我们仍然可以这样做,但希望找到更好的解析器。 – 2010-08-24 14:52:09

3

我写的JSON解析器是不是递归的几种语言,但是到现在为止没有在JavaScript。它不是递归的,而是使用一个名为stack的本地数组。在actionscript中,这比递归速度快得多,内存效率也更高,并且我认为javascript会类似。

此实现仅对具有反斜杠转义的带引号的字符串使用eval作为优化和简化。这可以很容易地用任何其他解析器的字符串处理来代替。转义处理代码很长,与递归无关。

此实现在(至少)以下方面不严格。它将8位字符视为空白。它允许在数字中引导“+”和“0”。它允许在数组和对象中跟踪“,”。它在第一个结果后忽略输入。所以“[+09,] 2”返回[9]并忽略“2”。

function parseJSON(inJSON) { 
    var result; 
    var parent; 
    var string; 

    var depth = 0; 
    var stack = new Array(); 
    var state = 0; 

    var began , place = 0 , limit = inJSON.length; 
    var letter; 

    while (place < limit) { 
     letter = inJSON.charCodeAt(place++); 

     if (letter <= 0x20 || letter >= 0x7F) { // whitespace or control 
     } else if (letter === 0x22) {    // " string 
      var slash = 0; 
      var plain = true; 

      began = place - 1; 
      while (place < limit) { 
       letter = inJSON.charCodeAt(place++); 

       if (slash !== 0) { 
        slash = 0; 
       } else if (letter === 0x5C) {  // \ escape 
        slash = 1; 
        plain = false; 
       } else if (letter === 0x22) {  // " string 
        if (plain) { 
         result = inJSON.substring(began + 1 , place - 1); 
        } else { 
         string = inJSON.substring(began , place); 
         result = eval(string); // eval to unescape 
        } 

        break; 
       } 
      } 
     } else if (letter === 0x7B) {    // { object 
      stack[depth++] = state; 
      stack[depth++] = parent; 
      parent = new Object(); 
      result = undefined; 
      state = letter; 
     } else if (letter === 0x7D) {    // } object 
      if (state === 0x3A) { 
       parent[stack[--depth]] = result; 
       state = stack[--depth]; 
      } 

      if (state === 0x7B) { 
       result = parent; 
       parent = stack[--depth]; 
       state = stack[--depth]; 
      } else { 
       // error got } expected state { 
       result = undefined; 
       break; 
      } 
     } else if (letter === 0x5B) {    // [ array 
      stack[depth++] = state; 
      stack[depth++] = parent; 
      parent = new Array(); 
      result = undefined; 
      state = letter; 
     } else if (letter === 0x5D) {    // ] array 
      if (state === 0x5B) { 
       if (undefined !== result) parent.push(result); 

       result = parent; 
       parent = stack[--depth]; 
       state = stack[--depth]; 
      } else { 
       // error got ] expected state [ 
       result = undefined; 
       break; 
      } 
     } else if (letter === 0x2C) {    // , delimiter 
      if (undefined === result) { 
       // error got , expected previous value 
       break; 
      } else if (state === 0x3A) { 
       parent[stack[--depth]] = result; 
       state = stack[--depth]; 
       result = undefined; 
      } else if (state === 0x5B) { 
       parent.push(result); 
       result = undefined; 
      } else { 
       // error got , expected state [ or : 
       result = undefined; 
       break; 
      } 
     } else if (letter === 0x3A) {    // : assignment 
      if (state === 0x7B) { 
       // could verify result is string 
       stack[depth++] = state; 
       stack[depth++] = result; 
       state = letter; 
       result = undefined; 
      } else { 
       // error got : expected state { 
       result = undefined; 
       break; 
      } 
     } else { 
      if ((letter >= 0x30 && letter <= 0x39) || letter === 0x2B || letter === 0x2D || letter === 0x2E) { 
       var    exponent = -2; 
       var    real = (letter === 0x2E); 
       var    digits = (letter >= 0x30 && letter <= 0x39) ? 1 : 0; 

       began = place - 1; 
       while (place < limit) { 
        letter = inJSON.charCodeAt(place++); 

        if (letter >= 0x30 && letter <= 0x39) {   // digit 
         digits += 1; 
        } else if (letter === 0x2E) {      // . 
         if (real) break; 
         else real = true; 
        } else if (letter === 0x45 || letter === 0x65) { // e E 
         if (exponent > began || 0 === digits) break; 
         else exponent = place - 1; 
         real = true; 
        } else if (letter === 0x2B || letter === 0x2D) { // + - 
         if (place != exponent + 2) break; 
        } else { 
         break; 
        } 
       } 

       place -= 1; 
       string = inJSON.substring(began , place); 

       if (0 === digits) break; // error expected digits 
       if (real) result = parseFloat(string); 
       else result = parseInt(string , 10); 
      } else if (letter === 0x6E && 'ull' === inJSON.substr(place , 3)) { 
       result = null; 
       place += 3; 
      } else if (letter === 0x74 && 'rue' === inJSON.substr(place , 3)) { 
       result = true; 
       place += 3; 
      } else if (letter === 0x66 && 'alse' === inJSON.substr(place , 4)) { 
       result = false; 
       place += 4; 
      } else { 
       // error unrecognized literal 
       result = undefined; 
       break; 
      } 
     } 

     if (0 === depth) break; 
    } 

    return result; 
}