JSON 验证器工作原理 - 从词法分析到语法树(2026 技术解析)
1935 字
10 分钟
JSON 验证器工作原理 - 从词法分析到语法树(2026 技术解析)
🔬 JSON 验证器是如何工作的?
当你将 JSON 数据粘贴到验证工具中时,背后发生了什么?本文将深入解析 JSON 验证器的内部工作原理。
graph LR
A[原始 JSON 文本] --> B[词法分析]
B --> C[Token 流]
C --> D[语法分析]
D --> E[语法树]
E --> F[语义验证]
F --> G[错误报告]
📚 编译原理基础
编译器的工作流程
源代码 → 词法分析 → 语法分析 → 语义分析 → 代码生成 ↓ ↓ ↓ Token 流 AST 中间代码JSON 验证器的工作流程与编译器类似,但更加简化。
🔍 第一阶段:词法分析(Lexical Analysis)
什么是词法分析?
词法分析(Lexical Analysis)是将字符流转换为 Token 流的过程。
输入:
{"name": "John", "age": 30}输出:
Token 1: L_BRACE {Token 2: STRING "name"Token 3: COLON :Token 4: STRING "John"Token 5: COMMA ,Token 6: STRING "age"Token 7: COLON :Token 8: NUMBER 30Token 9: R_BRACE }词法分析器实现
简化版词法分析器:
class Token { constructor(type, value, position) { this.type = type; this.value = value; this.position = position; }}
class Lexer { constructor(text) { this.text = text; this.pos = 0; this.currentChar = this.text[0]; this.tokens = []; }
error(message) { throw new Error(`词法错误:${message} 在位置 ${this.pos}`); }
advance() { this.pos++; this.currentChar = this.text[this.pos] || null; }
skipWhitespace() { while (this.currentChar && /\s/.test(this.currentChar)) { this.advance(); } }
readString() { const start = this.pos; this.advance(); // 跳过开头的 "
let result = ''; while (this.currentChar && this.currentChar !== '"') { if (this.currentChar === '\\') { this.advance(); if (this.currentChar) { result += this.currentChar; this.advance(); } } else { result += this.currentChar; this.advance(); } }
if (!this.currentChar) { this.error('未闭合的字符串'); }
this.advance(); // 跳过结尾的 " return new Token('STRING', result, start); }
readNumber() { const start = this.pos; let result = '';
while (this.currentChar && /[\d.\-eE+]/.test(this.currentChar)) { result += this.currentChar; this.advance(); }
return new Token('NUMBER', parseFloat(result), start); }
getNextToken() { // 跳过空白字符 this.skipWhitespace();
if (!this.currentChar) { return new Token('EOF', null, this.pos); }
const start = this.pos;
// 识别各种 Token switch (this.currentChar) { case '{': this.advance(); return new Token('L_BRACE', '{', start); case '}': this.advance(); return new Token('R_BRACE', '}', start); case '[': this.advance(); return new Token('L_BRACKET', '[', start); case ']': this.advance(); return new Token('R_BRACKET', ']', start); case ':': this.advance(); return new Token('COLON', ':', start); case ',': this.advance(); return new Token('COMMA', ',', start); case '"': return this.readString(); default: if (/[\d\-]/.test(this.currentChar)) { return this.readNumber(); } if (/[a-zA-Z]/.test(this.currentChar)) { return this.readKeyword(); } this.error(`未知字符:${this.currentChar}`); } }
readKeyword() { const start = this.pos; let result = '';
while (this.currentChar && /[a-zA-Z]/.test(this.currentChar)) { result += this.currentChar; this.advance(); }
// 识别 true, false, null if (result === 'true' || result === 'false') { return new Token('BOOLEAN', result, start); } if (result === 'null') { return new Token('NULL', result, start); }
this.error(`未知关键字:${result}`); }
tokenize() { while (true) { const token = this.getNextToken(); this.tokens.push(token); if (token.type === 'EOF') break; } return this.tokens; }}
// 使用示例const lexer = new Lexer('{"name": "John"}');const tokens = lexer.tokenize();console.log(tokens);🌳 第二阶段:语法分析(Syntax Analysis)
什么是语法分析?
语法分析(Syntax Analysis)是将 Token 流转换为抽象语法树(AST)的过程。
JSON 的语法规则(简化版):
json → object | arrayobject → L_BRACE (STRING COLON json (COMMA STRING COLON json)*)? R_BRACEarray → L_BRACKET (json (COMMA json)*)? R_BRACKET递归下降分析器
语法分析器实现:
class Parser { constructor(tokens) { this.tokens = tokens; this.pos = 0; this.currentToken = this.tokens[0]; }
error(message) { const token = this.currentToken; throw new Error( `语法错误:${message} 在第 ${token.position} 行` ); }
advance() { this.pos++; this.currentToken = this.tokens[this.pos] || new Token('EOF', null, this.pos); }
expect(type) { if (this.currentToken.type !== type) { this.error(`期望 ${type},但得到 ${this.currentToken.type}`); } this.advance(); }
parse() { if (this.currentToken.type === 'L_BRACE') { return this.parseObject(); } if (this.currentToken.type === 'L_BRACKET') { return this.parseArray(); } this.error('JSON 必须以 { 或 [ 开始'); }
parseObject() { const obj = {}; const startToken = this.currentToken;
this.expect('L_BRACE');
// 空对象 if (this.currentToken.type === 'R_BRACE') { this.advance(); return obj; }
// 解析键值对 while (true) { // 解析键 if (this.currentToken.type !== 'STRING') { this.error('对象的键必须是字符串'); } const key = this.currentToken.value; this.advance();
// 解析冒号 this.expect('COLON');
// 解析值 const value = this.parseValue(); obj[key] = value;
// 检查逗号或结束 if (this.currentToken.type === 'COMMA') { this.advance(); } else if (this.currentToken.type === 'R_BRACE') { this.advance(); break; } else { this.error('期望 , 或 }'); } }
return obj; }
parseArray() { const arr = [];
this.expect('L_BRACKET');
// 空数组 if (this.currentToken.type === 'R_BRACKET') { this.advance(); return arr; }
// 解析元素 while (true) { const value = this.parseValue(); arr.push(value);
// 检查逗号或结束 if (this.currentToken.type === 'COMMA') { this.advance(); } else if (this.currentToken.type === 'R_BRACKET') { this.advance(); break; } else { this.error('期望 , 或 ]'); } }
return arr; }
parseValue() { const token = this.currentToken;
switch (token.type) { case 'L_BRACE': return this.parseObject(); case 'L_BRACKET': return this.parseArray(); case 'STRING': this.advance(); return token.value; case 'NUMBER': this.advance(); return token.value; case 'BOOLEAN': this.advance(); return token.value === 'true'; case 'NULL': this.advance(); return null; default: this.error(`意外的 Token: ${token.type}`); } }}
// 使用示例const lexer = new Lexer('{"name": "John", "age": 30}');const tokens = lexer.tokenize();const parser = new Parser(tokens);const ast = parser.parse();console.log(ast);// 输出:{ name: 'John', age: 30 }⚠️ 第三阶段:错误检测
常见错误检测
1. 缺少逗号检测:
parseObject() { // ... while (true) { const key = this.currentToken.value; this.advance();
// 如果没有冒号,可能是缺少逗号 if (this.currentToken.type !== 'COLON') { if (this.currentToken.type === 'STRING') { this.error('缺少逗号分隔符'); } } // ... }}2. 尾随逗号检测:
parseArray() { // ... while (true) { const value = this.parseValue(); arr.push(value);
if (this.currentToken.type === 'COMMA') { this.advance(); // 检查是否是最后一个元素 if (this.currentToken.type === 'R_BRACKET') { this.error('JSON 不允许尾随逗号'); } } else if (this.currentToken.type === 'R_BRACKET') { this.advance(); break; } }}3. 引号不匹配检测:
readString() { const start = this.pos; this.advance(); // 跳过开头的 "
let result = ''; while (this.currentChar && this.currentChar !== '"') { if (this.currentChar === '\\') { this.advance(); if (this.currentChar) { result += this.currentChar; this.advance(); } } else { result += this.currentChar; this.advance(); } }
// 检查是否找到结束的引号 if (!this.currentChar) { this.error('未闭合的字符串,缺少结束引号'); }
this.advance(); // 跳过结尾的 " return new Token('STRING', result, start);}🌲 第四阶段:构建语法树
抽象语法树(AST)结构
示例 JSON:
{ "name": "John", "age": 30, "skills": ["js", "python"]}对应的 AST:
ObjectExpression├── Property: "name"│ └── StringLiteral: "John"├── Property: "age"│ └── NumberLiteral: 30└── Property: "skills" └── ArrayExpression ├── StringLiteral: "js" └── StringLiteral: "python"AST 节点类型
// AST 节点类型定义const NodeTypes = { OBJECT: 'ObjectExpression', ARRAY: 'ArrayExpression', PROPERTY: 'Property', STRING: 'StringLiteral', NUMBER: 'NumberLiteral', BOOLEAN: 'BooleanLiteral', NULL: 'NullLiteral'};
// 增强的解析器,构建完整 ASTclass ASTBuilder extends Parser { parseObject() { const node = { type: NodeTypes.OBJECT, properties: [], position: this.currentToken.position };
this.expect('L_BRACE');
if (this.currentToken.type !== 'R_BRACE') { do { const prop = this.parseProperty(); node.properties.push(prop);
if (this.currentToken.type === 'COMMA') { this.advance(); } else { break; } } while (true); }
this.expect('R_BRACE'); return node; }
parseProperty() { const key = this.currentToken.value; this.advance(); this.expect('COLON');
const value = this.parseValue();
return { type: NodeTypes.PROPERTY, key: key, value: value }; }}🔧 实际验证器实现
完整验证器
class JSONValidator { constructor() { this.errors = []; }
validate(text) { this.errors = [];
try { const lexer = new Lexer(text); const tokens = lexer.tokenize(); const parser = new ASTBuilder(tokens); const ast = parser.parse();
return { valid: true, ast: ast, errors: [] }; } catch (error) { this.errors.push({ message: error.message, position: error.position || 0, type: 'SyntaxError' });
return { valid: false, ast: null, errors: this.errors }; } }
validateWithDetails(text) { const result = this.validate(text);
if (!result.valid) { // 添加修复建议 result.errors.forEach(error => { if (error.message.includes('缺少逗号')) { error.suggestion = '在上一行末尾添加逗号'; } else if (error.message.includes('尾随逗号')) { error.suggestion = '移除最后一个元素后的逗号'; } else if (error.message.includes('未闭合')) { error.suggestion = '添加缺少的闭合符号'; } }); }
return result; }}
// 使用示例const validator = new JSONValidator();const result = validator.validateWithDetails('{"name": "John" "age": 30}');
if (!result.valid) { console.log('验证失败:'); result.errors.forEach(error => { console.log(`- ${error.message}`); if (error.suggestion) { console.log(` 建议:${error.suggestion}`); } });}📊 性能优化
1. 增量解析
对于大型 JSON 文件,可以实现增量解析:
class IncrementalParser { constructor() { this.cache = new Map(); }
parseChunk(chunk, offset) { const cacheKey = `${offset}-${chunk.length}`;
if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey); }
const result = this.parse(chunk); this.cache.set(cacheKey, result);
return result; }}2. 错误恢复
class ErrorRecoveryParser extends Parser { parseWithErrorRecovery() { const nodes = [];
try { while (this.currentToken.type !== 'EOF') { try { const node = this.parse(); nodes.push(node); } catch (error) { this.errors.push(error); this.recoverFromError(); } } } catch (fatalError) { this.errors.push(fatalError); }
return nodes; }
recoverFromError() { // 跳过错误的 Token,尝试恢复 while (this.currentToken.type !== 'EOF') { if (['L_BRACE', 'R_BRACE', 'L_BRACKET', 'R_BRACKET'].includes( this.currentToken.type )) { return; // 找到同步点 } this.advance(); } }}💬 总结
JSON 验证器的工作原理包含四个关键阶段:
- ✅ 词法分析:字符流 → Token 流
- ✅ 语法分析:Token 流 → 语法树
- ✅ 错误检测:识别语法错误
- ✅ 构建 AST:生成抽象语法树
核心技术:
- 正则表达式匹配 Token
- 递归下降语法分析
- 错误恢复机制
- 增量解析优化
实际应用:
- 在线 JSON 验证工具
- 编辑器语法高亮
- IDE 智能提示
- CI/CD 自动校验
相关资源
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
最后更新于 2026-03-29,距今已过 201 天
部分内容可能已过时
相关文章 智能推荐
1
JSON 过滤集成指南与工作流自动化
技术教程 JSON 过滤集成指南,掌握 Node.js 中间件、jq 脚本、Python 管道、Webpack 构建等自动化集成方案。
2
JSON 差异比较集成指南与工作流自动化
技术教程 JSON 差异比较集成指南,包括 CI/CD Pipeline、Node.js 脚本、定时任务、Kubernetes 配置审计等多种集成方案。
3
JSON 语法校验完整指南 - 8 大常见错误与解决方案(2026)
教程指南 JSON 语法校验完整指南,详解 8 种常见错误类型、自动检测工具、修复方案。包含代码示例、验证工具推荐,帮助开发者快速定位并解决 JSON 语法问题。
4
JSON 排序集成指南与工作流自动化
技术教程 JSON 排序集成指南,详解 Git Pre-commit、API 响应中间件、ETL 管道、CI 构建等 4 种自动化工作流集成方案。
5
JSON 路径测试集成指南与工作流自动化
技术教程 JSON 路径测试集成指南,学习如何将 JSONPath 测试集成到自动化测试、CI/CD 和数据管道中,实现数据提取逻辑的自动校验。
随机文章 随机推荐