代码之家  ›  专栏  ›  技术社区  ›  Lou Franco

非递归Javascript JSON解析器

  •  16
  • Lou Franco  · 技术社区  · 15 年前

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

    我看过一些标准的JavaScriptJSON解析器,它们是递归的。想知道是否有人知道任何安全且非递归的JSON解析器。我希望它有更少的特性——我只是有一个巨大的对象数组。

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

    编辑:进一步检查时,解析器内部使用的eval()会引发堆栈溢出。所以,它必须是递归的。

    4 回复  |  直到 14 年前
        1
  •  0
  •   Nakedible    15 年前

    浏览器中的JSON解析通常只使用eval完成,但在eval前面使用正则表达式“lint”,这应该可以安全地评估JSON。

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

        2
  •  5
  •   Lou Franco    15 年前

    如果eval抛出stackoverflow,您可以使用它

    http://code.google.com/p/json-sans-eval/

    一个根本不使用eval()的JSON解析器。

        3
  •  3
  •   drawnonward    14 年前

    我已经用几种语言编写了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;
    }
    
        4
  •  1
  •   Ramon Araujo    15 年前

    我建议您将JSON字符串分成块,并按需提供。可能也在使用Ajax,您可以有一个刚好适合您需要的配方。 使用“分而治之”机制,我认为您仍然可以使用常见的JSON解析方法。

    希望能有所帮助,