2010-08-24 38 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; 
}