нерекурсивный анализатор JSON JavaScript - PullRequest
16 голосов
/ 24 августа 2010

У меня очень большая строка JSON, которую мне нужно проанализировать с помощью JavaScript в браузере. Прямо сейчас, в нескольких браузерах, у меня не хватает стека. К сожалению, мой JSON может содержать пользовательские строки, поэтому я не могу использовать eval или иным образом позволить браузеру разобрать его.

Я рассмотрел несколько стандартных JSON-анализаторов JavaScript, и они рекурсивны. Хотите знать, если кто-нибудь знает какой-либо парсер JSON, который является безопасным и не рекурсивным. Я бы хотел, чтобы у него было меньше возможностей - у меня просто огромный массив объектов.

В качестве альтернативы, если кто-то знает тот, который может быть легко модифицирован, это тоже очень поможет.

EDIT: При ближайшем рассмотрении переполнение стека генерируется функцией eval (), используемой внутри синтаксического анализатора. Итак, это должно быть рекурсивно.

Ответы [ 4 ]

5 голосов
/ 24 августа 2010

Если eval выбрасывает stackoverflow, вы можете использовать это

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

Анализатор JSON, который вообще не использует eval ().

3 голосов
/ 04 сентября 2011

Я написал парсеры json, которые не являются рекурсивными на нескольких языках, но до сих пор не в javascript.Вместо того чтобы быть рекурсивным, здесь используется локальный массив с именем stack.В ActionScript это было значительно быстрее и более эффективно для использования памяти, чем рекурсия, и я предполагаю, что javascript будет похожим.

В этой реализации eval используется только для строк в кавычках с обратными слешами, как для оптимизации, так и для упрощения.Это можно легко заменить обработкой строк из любого другого парсера.Код обработки escape длинный и не связан с рекурсией.

Эта реализация не является строгой (по крайней мере) в следующих отношениях.Он обрабатывает 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;
}
1 голос
/ 24 августа 2010

Я рекомендую вам разбить строку JSON на куски и вывести их по требованию. Возможно, вы тоже используете AJAX, у вас может быть рецепт, который точно соответствует вашим потребностям. Используя механизм «разделяй и властвуй», я думаю, что вы все еще можете использовать общие методы синтаксического анализа JSON.

Надеюсь, это поможет,

0 голосов
/ 24 августа 2010

Синтаксический анализ JSON в браузере обычно выполняется просто с помощью eval, но перед ним стоит регулярное выражение «lint», что должно обеспечить безопасность при оценке JSON.в Википедии:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...