JSON.parse 抛出的错误长这样:Unexpected token , in JSON at position 128。position 是从 0 开始的字符偏移,把它换算成行列就能直接跳到出错的位置:
function parseWithPos(text) {
try {
return JSON.parse(text);
} catch (e) {
const m = /position (\d+)/.exec(e.message);
if (!m) throw e;
const pos = Number(m[1]);
const before = text.slice(0, pos);
const line = before.split('\n').length;
const col = pos - before.lastIndexOf('\n');
throw new Error('第 ' + line + ' 行第 ' + col + ' 列:' + e.message);
}
}
最常见的六种非法 JSON
- 尾随逗号:
{"a":1,}。JavaScript 对象字面量允许,JSON 不允许。 - 单引号:
{'a':1}。JSON 只认双引号,键也一样。 - 注释:JSON 标准里没有注释,接口返回带注释一定是手写的。
- NaN / Infinity / undefined:都不是合法 JSON 值。序列化前要转成 null 或字符串。
- BOM 头:文件开头多了
\uFEFF,表现为第一个字符就报错。读文件时先 strip 掉。 - 未转义的控制字符:字符串里混进真实的换行或制表符,必须写成
\n、\t。
还有一个更隐蔽的问题:大整数精度
JSON 里的数字会被转成双精度浮点,超过 2 的 53 次方就丢精度。典型场景是雪花算法生成的 19 位用户 ID:{"id": 1234567890123456789} 解析出来会变成 1234567890123456800,末尾几位直接变了。
解决办法是让服务端把这个字段序列化成字符串。这不是前端能补救的问题——精度在解析那一刻就丢了。
JSON.stringify 也会坑人
- 值为 undefined、函数、Symbol 的键会被直接丢弃,数组里则变成 null。
- 循环引用会抛
TypeError,需要自己写 replacer 或者用 WeakSet 记录。 - Date 对象会自动调用 toJSON 变成 ISO 字符串,时区信息可能和你预期的不一样。
- NaN 和 Infinity 会被转成 null,且不会报错——这是最容易被忽视的一条。
调试时把格式化后的结果和原文对照,比盯着一行压缩 JSON 找逗号快得多。用JSON 格式化把内容贴进去,出错位置会直接标出来。