非递归JavaScript JSON解析器

时间:2010-08-24 14:27:16

标签: javascript json

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

我查看了一些标准的JavaScript JSON解析器,它们是递归的。想知道是否有人知道任何安全且非递归的JSON解析器。我愿意让它拥有更少的功能 - 我只有一大堆物体。

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

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

4 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分: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;
}

答案 2 :(得分:1)

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

希望有所帮助,

答案 3 :(得分:0)

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

维基百科上有一个例子: