JSON 到底是什么?
JSON(JavaScript Object Notation)是 RFC 8259 定义的文本数据格式。虽然名字里有 JavaScript,但它跟运行 JS 代码没关系——只是一种用纯文本表示结构化数据的方式。所有主流编程语言都能读写它。
这个格式大约在 2001 年由 Douglas Crockford 正式化,但真正流行起来是因为它比 XML 简单,同时又足够人类可读,能直接用肉眼调试。今天它是 REST API 的默认传输格式、配置文件(package.json、tsconfig.json)的标准格式,也是 MongoDB 等文档数据库的存储格式。
一个让很多人意外的事实:JSON 是 YAML 1.2 的严格子集。任何合法的 JSON 文件同时也是合法的 YAML。反过来不成立——YAML 支持注释、多行字符串和锚点,这些 JSON 都不支持。
JSON 语法规则(真正重要的那些)
JSON 只有 6 种数据类型:字符串、数字、布尔值(true/false)、null、对象(键值对)和数组(有序列表)。就这些。没有日期类型,没有 undefined,没有函数。
字符串必须用双引号。单引号是非法的。这是人们把 JavaScript 对象粘贴到 JSON 文件时出现 "unexpected token" 错误的第一大原因。
数字不能有前导零(01 非法),不能用十六进制(0xFF 非法),不能是 NaN 或 Infinity。如果需要这些值,把它们编码为字符串。
对象用花括号 {} 包裹,键值对用冒号分隔。键必须是双引号字符串。最后一项后面的尾逗号是非法的——这是手动编辑 JSON 时最常见的语法错误。
// ❌ 无效——这些错误到处都是
{
name: "Alice", // 键必须加引号
'age': 30, // 不允许单引号
"scores": [1, 2, 3,], // 尾逗号
"id": 0x1F, // 不允许十六进制
}
// ✅ 合法 JSON
{
"name": "Alice",
"age": 30,
"scores": [1, 2, 3],
"id": 31
}5 个最常见的 JSON 错误(以及怎么修)
1. 尾逗号:删掉对象或数组最后一项后面的逗号。大多数格式化工具会立即高亮这个错误。如果你想要尾逗号,在 VS Code 设置中使用 JSONC(JSON with Comments)格式。
2. Unexpected token at position 0:通常意味着响应根本不是 JSON——可能是 HTML 错误页面(以 < 开头)或空字符串。检查你的 API 端点和 Content-Type 头。
3. 未加引号的键:从 JavaScript 控制台复制输出会得到 {name: "value"} 而不是 {"name": "value"}。用格式化工具自动加上引号。
4. 单引号字符串:Python 的 json.dumps() 总是产生双引号,但如果你手写 JSON 或从 Python repr() 复制,可能会有单引号。简单的查找替换在字符串包含撇号时会出问题——用专业的格式化工具。
5. BOM(字节顺序标记):某些 Windows 编辑器会在文件开头加一个不可见的 \uFEFF 字符。JSON 解析器会在这里卡住。用十六进制编辑器打开文件——如果开头是 EF BB BF,那就是 BOM。另存为不带 BOM 的 UTF-8。
JSON vs JSONC vs JSON5 vs YAML:什么时候用什么
标准 JSON(RFC 8259):用于 API 载荷、数据交换,以及任何需要最大兼容性的场景。每种语言的每个解析器都支持它。
JSONC(JSON with Comments):用于你需要手动编辑的配置文件——VS Code 设置、tsconfig.json 等。允许 // 和 /* */ 注释以及尾逗号。标准的 JSON.parse() 不支持。
JSON5:扩展了 JSON,支持不加引号的键、单引号字符串、十六进制数字、尾逗号和注释。适合人工编辑的配置文件。需要 json5 npm 包来解析。
YAML:当你需要注释、多行字符串或锚点/别名来实现 DRY 配置时使用。在 DevOps 中很常见(Docker Compose、Kubernetes、GitHub Actions)。注意:缩进错误是无声的,而且致命。"挪威问题"(NO 被解析为 false)坑过很多团队。
我们的建议:机器读的东西用标准 JSON。人经常编辑的东西用 JSONC 或 YAML。除非你的团队已经依赖 JSON5,否则别用它。
处理大型 JSON 文件
浏览器端工具(包括我们的)能处理大约 50 MB 以内的文件,再大标签页就会开始卡顿。更大的文件用命令行工具:jq 做查询,python -m json.tool 做格式化,fx 做交互式浏览。
如果你经常处理超过 100 MB 的 JSON 文件,考虑一下 JSON 是不是正确的格式。NDJSON(换行分隔的 JSON,每行一个对象)是可流式处理的,不需要把整个文件加载到内存。jq 等工具可以逐行处理 NDJSON。
对于 API 响应,分页是你的朋友。如果一个端点在一次响应中返回 10,000 个对象,那是 API 设计问题,不是 JSON 的问题。请求更小的页面,增量处理。
性能提示:V8 中 JSON.parse() 对大对象实际上比 JavaScript 对象字面量更快(反直觉但自 2019 年以来确实如此)。如果你的前端代码中有大型静态数据集,用 JSON.parse('...') 包裹它可以改善启动时间。
JSON 安全注意事项
永远不要用 eval() 解析 JSON。这在 2000 年代初 JSON.parse() 还不存在时很常见,但它会执行任意代码。始终使用 JSON.parse() 或你所用语言的等价方法。
注意 JavaScript 中的原型污染。如果你解析 {"__proto__": {"isAdmin": true}} 并将其合并到对象中,可能会意外修改 Object.prototype。lodash.merge 等库曾有此漏洞。对不受你控制的解析数据使用 Object.create(null)。
JSON 不支持循环引用。如果你尝试 JSON.stringify() 一个引用自身的对象,会得到 TypeError。使用 replacer 函数或 flatted 等库来处理循环结构。
大小限制对 API 很重要。10 MB 的 JSON 载荷可能成为拒绝服务攻击向量。在服务器上设置 Content-Length 限制(Express: express.json({limit: "1mb"}),nginx: client_max_body_size)。
在不同场景中验证 JSON
快速验证:粘贴到我们的 JSON 格式化工具。它会显示语法错误的精确行号和字符位置,能处理最大 50 MB 的文件,而且离线可用。
Schema 验证:使用 JSON Schema(draft 2020-12)定义你的 JSON 应该遵循的结构。ajv(JavaScript)或 jsonschema(Python)等工具在运行时根据 schema 验证数据。这能捕获"语法正确但结构错误"的问题。
CI/CD 流水线:添加 JSON lint 步骤。最简单的方法:python -m json.tool < file.json > /dev/null。需要更严格的 schema 验证时,用 ajv-cli 或 check-jsonc-syntax。
配置文件:大多数框架会验证自己的配置格式。运行 next build 能发现 next.config.js 问题,tsc --noEmit 能发现 tsconfig.json 问题。不要重新发明你的工具链已经提供的验证。