我寫的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;
}
那麼,遞歸編碼與否,解析一個深度嵌套的結構必須經過相同的過程。你確定這個結構真的有效嗎? 「非常大」有多大? – Pointy 2010-08-24 14:44:21
這是合法的 - 它是一個包含幾千個對象的數組。當它達到一定的大小時,eval()不能解析它(IE可以,但其他瀏覽器不能) – 2010-08-24 14:54:31
@Pointy yes它會經歷相同的過程,但它不需要使用堆棧可能會耗盡空間。據推測,一個非遞歸的在堆中建立了一些其他等效的數據結構(希望它更大) – 2010-08-24 14:55:51