Я написал парсеры 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;
}