2010-08-24 9 views
16

Tengo una cadena JSON muy grande que necesito analizar con JavaScript en el navegador. En este momento, en algunos navegadores, me quedo sin espacio en la pila. Lamentablemente, mi JSON puede contener cadenas de usuario, por lo que no puedo usar eval o dejar que el navegador lo analice.JavaScript no recursivo Analizador JSON

He visto algunos de los analizadores JSON estándar de JavaScript, y son recursivos. Preguntándose si alguien sabe de algún analizador JSON que sea seguro y no recursivo. Estaría dispuesto a que tenga menos funciones. Solo tengo una gran variedad de objetos.

Alternativamente, si alguien sabe de uno que podría ser fácil de modificar, sería una gran ayuda también.

EDITAR: Al realizar una inspección más cercana, eval() utiliza el desbordamiento de pila utilizado dentro del analizador. Por lo tanto, debe ser recursivo.

+0

Bien, recursivamente codificado o no, el análisis de una estructura profundamente anidada prácticamente tiene que pasar por el mismo proceso. ¿Estás seguro de que la estructura es realmente válida? ¿Qué tan grande es "muy grande"? – Pointy

+0

Es legal, es una matriz de algunos miles de objetos. Cuando llega a un determinado tamaño, eval() no puede analizarlo (bueno, IE puede, pero otros navegadores no pueden) –

+0

@Pointy sí, pasaría por el mismo proceso, pero no necesita usar el pila, que podría quedarse sin espacio.Presumiblemente, uno no recursivo construye alguna otra estructura de datos equivalente en el montón (que es más grande, con suerte) –

Respuesta

0

El análisis de JSON en el navegador se realiza generalmente con solo eval, pero precediendo al eval con una expresión regular "lint", que se supone que hace que sea seguro evaluar el JSON.

Hay un ejemplo de esto en wikipedia:

+1

a la derecha, ahora veo que eval se llama después de verificar la seguridad, y luego arroja un desbordamiento de pila (porque es internamente recursivo y la cadena JSON es grande) –

+0

Gracias: su sugerencia me ayudó a ver que el problema real estaba en eval(). –

1

Le recomiendo que divida la cadena JSON en trozos, y los ponga a pedido. Puede estar usando AJAX también, puede tener una receta que se ajuste a sus necesidades. Usando un mecanismo de "divide y vencerás", creo que todavía puedes usar métodos de análisis JSON comunes.

Espero que ayude,

+0

Lo consideramos, pero es difícil debido a otras limitaciones. Necesitamos enviar la respuesta todo de una vez. Para delimitarlo para que funcione una división(), se requiere otro pase de escape. Todavía podríamos ir por este camino, pero con la esperanza de encontrar un analizador mejor. –

3

He escrito analizadores JSON que no son recursiva en varios idiomas, pero hasta ahora no en javascript. En lugar de ser recursivo, utiliza una matriz local llamada pila. En ActionScript esto fue sustancialmente más rápido y más eficiente en cuanto a la memoria que la recursión, y supongo que javascript sería similar.

Esta implementación usa eval solo para cadenas entre comillas con escapes de barra invertida, como una optimización y simplificación. Eso podría reemplazarse fácilmente con el manejo de cadenas de cualquier otro analizador. El código de manejo de escape es largo y no está relacionado con la recursión.

Esta implementación no es estricta en (al menos) las siguientes formas. Trata caracteres de 8 bits como espacios en blanco. Permite "+" y "0" en números. Permite seguir "," en matrices y objetos. Ignora la entrada después del primer resultado. Entonces, "[+09,] 2" devuelve [9] e ignora "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; 
}