JSON 常见错误与陷阱
语法错误只是入门级问题,真正难查的是那些「解析成功但结果不对」的坑。
一、JSON 不是 JavaScript 对象字面量
这是 90% 语法错误的来源。JSON 比 JS 的对象字面量严格得多,下面五种写法在 JS 里合法,在 JSON 里全部报错:
| 写法 | 示例 | 原因 |
|---|---|---|
| 键不加引号 | {name: "a"} | 键必须是双引号字符串 |
| 单引号字符串 | {'name': 'a'} | 只允许双引号 |
| 尾逗号 | {"a":1,} | 最后一项后不能再有逗号 |
| 注释 | {"a":1 // 说明} | JSON 标准不支持注释 |
| undefined | {"a": undefined} | 只允许 null |
// 错误:三处问题——单引号、无引号的键、尾逗号
{ name: 'demo', version: '1.0', }
// 正确
{ "name": "demo", "version": "1.0" }
二、报错信息只说「位置」怎么办
传统 JSON.parse 的报错形如 Unexpected token } in JSON at position 42。position 是字符下标不是行号,需要自己数换行才能换算成「第几行第几列」。嵌套很深的配置里这一步非常费神。
本站在 JSON 工具里内置了一个独立扫描器,会直接告诉你「第 X 行第 Y 列,错在什么字符」,并画出插入符标记位置——不依赖浏览器报错格式。
三、精度丢失:最隐蔽的坑
JavaScript 的 Number 是 IEEE 754 双精度,安全整数上限是 253-1(9007199254740991)。超过这个范围就会静默丢失精度:
JSON.parse('{"id": 9007199254740993}').id
// 实际得到 9007199254740992,最后一位被吃掉了
典型受害者:雪花算法 ID、Twitter/微博 ID、超过 16 位的订单号。后端返回数字,前端 JSON.parse 一解析就变了,而且不报错。
三种解法:
- 后端把长整型序列化成字符串(最推荐);
- 前端用
JSON.parse(text, reviver)配合BigInt处理特定位数; - 使用支持大数的解析库(如
json-bigint)。
四、null 和「字段不存在」不是一回事
在 JSON 里,{"a": null} 表示「字段存在,值是空」;而 {} 表示「字段根本不存在」。很多接口用前者表示「显式清空」,用后者表示「不修改」,混用会导致更新逻辑出错。
// PATCH 请求里这两个语义完全不同
{"nickname": null} // 把昵称清空
{} // 不改动昵称
JS 侧判断也要注意:'a' in obj 能区分两种,而 obj.a === undefined 不行——因为 {"a": null} 取出来是 null 不是 undefined,但如果字段不存在取出来也是 undefined,很容易写成同一个分支。
五、JSON 没有日期类型
标准里只有字符串、数字、布尔、null、对象、数组六种类型。日期只能靠约定,业界默认用 ISO 8601:
"2026-11-01T13:45:00Z" // UTC
"2026-11-01T13:45:00+08:00" // 东八区
"2026-11-01" // 仅日期
用时间戳数字也可以,但必须约定单位是秒还是毫秒——这是接口联调最常吵的问题之一。
六、其他容易忽略的点
- 循环引用会直接抛错:
JSON.stringify遇到对象引用自己时报Converting circular structure to JSON,需要自定义 replacer 或先用结构化克隆。 - 函数和 Symbol 会被丢弃:
stringify时静默消失,不报错,容易造成「数据莫名少字段」。 - 中文不必转义:
\u4e2d\u6587完全合法但可读性差,存储时直接写中文更省体积(UTF-8 下 1 个汉字 3 字节,转义后 6 字节)。 - 顶层可以是任意值:早期标准要求顶层必须是对象或数组,现在
"abc"、123也合法,但部分老旧解析器仍会拒绝。