实现一个JSON.parse
手写实现JSON字符串解析函数,将JSON格式字符串转换为JavaScript对象
问题
实现一个自定义的JSON解析函数,能够将符合JSON格式的字符串转换为对应的JavaScript数据类型(对象、数组、字符串、数字、布尔值、null等)。这道题考察对JSON格式规范的理解以及字符串解析能力。
解答
/**
* 实现JSON.parse - 方法一:使用eval(不推荐)
* 注意:eval存在安全风险,仅用于理解原理
*/
function jsonParse1(jsonStr) {
return eval('(' + jsonStr + ')');
}
/**
* 实现JSON.parse - 方法二:使用Function构造函数(相对安全)
*/
function jsonParse2(jsonStr) {
return new Function('return ' + jsonStr)();
}
/**
* 实现JSON.parse - 方法三:手动解析(推荐)
* 完整实现词法分析和语法分析
*/
function jsonParse(jsonStr) {
let i = 0; // 当前解析位置
// 解析值的入口函数
function parseValue() {
skipWhitespace();
const char = jsonStr[i];
if (char === '{') return parseObject();
if (char === '[') return parseArray();
if (char === '"') return parseString();
if (char === 't') return parseTrue();
if (char === 'f') return parseFalse();
if (char === 'n') return parseNull();
if (char === '-' || (char >= '0' && char <= '9')) return parseNumber();
throw new SyntaxError(`Unexpected token at position ${i}`);
}
// 跳过空白字符
function skipWhitespace() {
while (i < jsonStr.length && /\s/.test(jsonStr[i])) {
i++;
}
}
// 解析对象
function parseObject() {
const obj = {};
i++; // 跳过 '{'
skipWhitespace();
// 空对象
if (jsonStr[i] === '}') {
i++;
return obj;
}
while (i < jsonStr.length) {
skipWhitespace();
// 解析键(必须是字符串)
if (jsonStr[i] !== '"') {
throw new SyntaxError(`Expected string key at position ${i}`);
}
const key = parseString();
skipWhitespace();
// 解析冒号
if (jsonStr[i] !== ':') {
throw new SyntaxError(`Expected ':' at position ${i}`);
}
i++;
// 解析值
const value = parseValue();
obj[key] = value;
skipWhitespace();
// 检查是否结束或继续
if (jsonStr[i] === '}') {
i++;
return obj;
}
if (jsonStr[i] === ',') {
i++;
continue;
}
throw new SyntaxError(`Expected ',' or '}' at position ${i}`);
}
throw new SyntaxError('Unexpected end of JSON input');
}
// 解析数组
function parseArray() {
const arr = [];
i++; // 跳过 '['
skipWhitespace();
// 空数组
if (jsonStr[i] === ']') {
i++;
return arr;
}
while (i < jsonStr.length) {
const value = parseValue();
arr.push(value);
skipWhitespace();
if (jsonStr[i] === ']') {
i++;
return arr;
}
if (jsonStr[i] === ',') {
i++;
continue;
}
throw new SyntaxError(`Expected ',' or ']' at position ${i}`);
}
throw new SyntaxError('Unexpected end of JSON input');
}
// 解析字符串
function parseString() {
let str = '';
i++; // 跳过开始的 '"'
while (i < jsonStr.length) {
const char = jsonStr[i];
if (char === '"') {
i++;
return str;
}
// 处理转义字符
if (char === '\\') {
i++;
const escapeChar = jsonStr[i];
const escapeMap = {
'"': '"',
'\\': '\\',
'/': '/',
'b': '\b',
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t'
};
if (escapeChar in escapeMap) {
str += escapeMap[escapeChar];
i++;
} else if (escapeChar === 'u') {
// Unicode转义
i++;
const unicode = jsonStr.substr(i, 4);
str += String.fromCharCode(parseInt(unicode, 16));
i += 4;
} else {
throw new SyntaxError(`Invalid escape character at position ${i}`);
}
} else {
str += char;
i++;
}
}
throw new SyntaxError('Unterminated string');
}
// 解析数字
function parseNumber() {
let numStr = '';
if (jsonStr[i] === '-') {
numStr += '-';
i++;
}
// 整数部分
while (i < jsonStr.length && jsonStr[i] >= '0' && jsonStr[i] <= '9') {
numStr += jsonStr[i];
i++;
}
// 小数部分
if (jsonStr[i] === '.') {
numStr += '.';
i++;
while (i < jsonStr.length && jsonStr[i] >= '0' && jsonStr[i] <= '9') {
numStr += jsonStr[i];
i++;
}
}
// 指数部分
if (jsonStr[i] === 'e' || jsonStr[i] === 'E') {
numStr += jsonStr[i];
i++;
if (jsonStr[i] === '+' || jsonStr[i] === '-') {
numStr += jsonStr[i];
i++;
}
while (i < jsonStr.length && jsonStr[i] >= '0' && jsonStr[i] <= '9') {
numStr += jsonStr[i];
i++;
}
}
return parseFloat(numStr);
}
// 解析true
function parseTrue() {
if (jsonStr.substr(i, 4) === 'true') {
i += 4;
return true;
}
throw new SyntaxError(`Invalid token at position ${i}`);
}
// 解析false
function parseFalse() {
if (jsonStr.substr(i, 5) === 'false') {
i += 5;
return false;
}
throw new SyntaxError(`Invalid token at position ${i}`);
}
// 解析null
function parseNull() {
if (jsonStr.substr(i, 4) === 'null') {
i += 4;
return null;
}
throw new SyntaxError(`Invalid token at position ${i}`);
}
return parseValue();
}
使用示例
// 示例1:解析基本类型
console.log(jsonParse('123')); // 123
console.log(jsonParse('"hello"')); // "hello"
console.log(jsonParse('true')); // true
console.log(jsonParse('null')); // null
// 示例2:解析对象
const objStr = '{"name":"张三","age":25,"isStudent":false}';
console.log(jsonParse(objStr));
// { name: '张三', age: 25, isStudent: false }
// 示例3:解析数组
const arrStr = '[1, 2, 3, "test", true, null]';
console.log(jsonParse(arrStr));
// [1, 2, 3, "test", true, null]
// 示例4:解析嵌套结构
const complexStr = `{
"user": {
"name": "李四",
"hobbies": ["reading", "coding"],
"address": {
"city": "北京",
"code": 100000
}
},
"count": 42
}`;
console.log(jsonParse(complexStr));
// 示例5:解析带转义字符的字符串
const escapeStr = '{"text":"Hello\\nWorld\\t\\"Quote\\""}';
console.log(jsonParse(escapeStr));
// { text: 'Hello\nWorld\t"Quote"' }
// 示例6:解析科学计数法
console.log(jsonParse('1.23e10')); // 12300000000
console.log(jsonParse('-2.5e-2')); // -0.025
// 对比原生JSON.parse
const testStr = '{"a":1,"b":[2,3],"c":{"d":4}}';
console.log('自定义:', jsonParse(testStr));
console.log('原生:', JSON.parse(testStr));
关键点
-
三种实现方式:eval(不安全)、Function构造函数(相对安全)、手动解析(最安全且完整)
-
递归下降解析:采用递归下降的方式解析JSON结构,每种数据类型对应一个解析函数
-
词法分析:通过游标(索引)逐字符扫描,识别不同的token类型(对象、数组、字符串、数字等)
-
空白字符处理:JSON允许在值之间存在空白字符(空格、换行、制表符),需要正确跳过
-
字符串转义:正确处理转义字符(
\n、\t、\"等)和Unicode转义(\uXXXX) -
数字格式:支持整数、小数、负数和科学计数法(如
1.23e10) -
错误处理:在解析失败时抛出有意义的错误信息,指明错误位置
-
边界情况:处理空对象
{}、空数组[]、嵌套结构等特殊情况 -
性能考虑:手动解析虽然代码较多,但避免了eval的安全风险,且性能可控
目录