JSON 解析报错怎么修:Unexpected token 逐条排查
结论:把浏览器、Node、Java、Python 的报错原文翻译成「第几行第几个字符出了什么问题」,再按尾逗号、单引号、注释、控制字符、BOM、截断六个方向逐个排除。

结论:报 parse 错误时先看报错的行列号定位第一个非法字符,再判断是缺引号、缺逗号、尾逗号还是 BOM 或内容截断,不要从头重读整段。
报错信息从来不告诉你「哪里错了」,它只告诉你「解析器走到哪走不下去了」。这两个位置经常不是同一个地方。
一、先把报错翻译成坐标
同一份坏 JSON,在不同语言里报的错完全不同,但都能换算成「第几行、第几个字符」:
| 环境 | 典型报错 | 怎么读 |
|---|---|---|
| 浏览器 / Node | Unexpected token } in JSON at position 42 |
position 是从 0 开始的字符偏移,不是行号 |
| Node 20+ | Unexpected non-whitespace character after JSON at position 42 |
说明前半段合法,多余内容在 42 之后 |
| Java Jackson | Unexpected character ('}' (code 125)): was expecting ... at [Source: (String)..."..." ; line: 3, column: 1] |
直接给了行列,最好用 |
| Python | json.decoder.JSONDecodeError: Expecting ',' delimiter: line 3 column 1 (char 42) |
char 是偏移,line/column 才是重点 |
| Go | invalid character '}' looking for beginning of object key string |
只给字符,需要自己回退定位 |
拿到 position 之后,把 JSON 粘进 JSON 在线解析,工具会直接把光标停在出错位置并标红行列号——这一步能省掉 90% 的肉眼数数。
二、六类高频错误
1. 尾逗号
{
"name": "json",
"version": 1,
}
} 前多了一个逗号。JSON 规范不允许尾逗号,JavaScript 对象字面量允许,所以从代码里复制出来的配置最容易带这个。修复:删掉最后一个逗号;如果这份数据本来就在 JS 文件里,改用 JSON5 转换 而不是硬套 JSON。
2. 单引号与不加引号的键
{ 'name': 'json' }
{ name: "json" }
JSON 的字符串只能用双引号,键也必须是双引号字符串。这两条是 JSON5 / JavaScript 的宽松语法,不是 JSON。字符串内部的双引号要写成 \",反斜杠写成 \\。
3. 注释
{
// 超时时间,单位毫秒
"timeout": 3000
}
RFC 8259 的语法里没有注释位置。要么删注释,要么改存 .jsonc / .json5 并用支持注释的解析器。各种「注释方案」的取舍见 JSON 注释怎么写。
4. 字符串里的裸换行与控制字符
{"sql": "SELECT *
FROM t"}
JSON 字符串内不允许直接出现换行、制表符和 U+0000–U+001F 控制字符,必须写成 \n、\t、\u0009。多行文本塞进 JSON 时,先在编辑器里做一次转义,或者用 JSON 压缩与转义 统一处理。
5. BOM 头
Windows 记事本保存的 UTF-8 会带 EF BB BF 三个字节。JSON.parse 遇到 BOM 会直接报 Unexpected token,而肉眼完全看不出来。判断方法:用十六进制查看文件头,或者把内容复制到在线工具里看第一个字符是否显示为 ``。修复:另存为「UTF-8 无 BOM」。
6. 内容被截断
日志只打印了前 4096 个字符、接口被网关截断、下载中断——这类报错的特征是「错误位置在末尾」且括号数量不匹配。先数括号:{、[ 的闭合数量对不上,就是截断而不是语法错。这时候不要修,去拿完整数据。
三、中文与转义相关的两类假故障
中文变成 \u4e2d\u6587:这是合法的 JSON 转义,不是乱码。解析后取值会还原成中文,只有直接看原始字符串时才是 \u 形式。需要给人看的时候用 格式化工具 输出即可;需要在 URL 或日志里保持中文,才需要关心转义策略。
中文变成乱码 涓枃:这是编码问题,不是 JSON 问题。UTF-8 字节被按 GBK 解码就会出现这种「三个字符顶一个汉字」的规律性乱码。检查响应头的 Content-Type: application/json; charset=utf-8,以及读文件时有没有显式指定 encoding='utf-8'。
四、五分钟修复流程
- 贴进工具:先用 在线解析 定位第一个错误位置,一次只修一个;
- 修完再校验:每修一处重新解析,避免一次改多处导致新错误覆盖旧错误;
- 压缩一次:用 压缩 去掉空白再解析,能区分「结构错」和「空白/换行引起的问题」;
- 结构对比:如果是改动后突然报错,用 JSON 对比 和上一版比,差异往往就是那一个逗号;
- 字段级校验:语法通过但业务报错(缺字段、类型不对),是 JSON Schema 校验 的范畴,不是语法问题。
五、为什么「看起来一样」的两段 JSON 一段能解析
最常见的原因是不可见字符:全角逗号 ,、全角引号 “”、不换行空格 U+00A0、零宽空格 U+200B。它们和半角字符在屏幕上几乎无法区分,但都不是 JSON 语法的一部分。
判断办法只有一个:把出错字符前后的内容复制出来,逐字节看码点。在浏览器控制台里执行下面这段,能直接列出所有非 ASCII 且不属于中文区间的字符:
[...text].filter((ch) => {
const code = ch.codePointAt(0);
return code > 127 && !(code >= 0x4e00 && code <= 0x9fff);
});
输出里出现 0x201c(左双引号)、0xff0c(全角逗号)、0x00a0,就是它们的问题。
六、预防比修复便宜
- 配置类文件在提交前跑一次解析校验,CI 里一行
node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))"就够; - 接口返回统一用序列化库生成,不要手写拼接字符串——手拼是尾逗号和漏引号的唯一来源;
- 日志打印 JSON 时先
JSON.stringify,不要直接模板拼接,避免半截内容被当成完整报文。
常见问题
如何快速定位 JSON 语法错误的位置?
先拿解析器给出的 position 或 line/column,position 是从 0 开始的字符偏移,不是行号;Java Jackson 与 Python 报告里会直接给出行列。把 JSON 粘进 JSON 在线解析,工具会把光标停在出错位置并标红行列号,能省掉大部分肉眼数数。若错误出现在开头,先检查文件头是否带 BOM,以及有没有不可见字符。
为什么从代码里复制出来的配置最容易带尾逗号?
JSON 规范不允许尾逗号,而 JavaScript 对象字面量允许,从 JS 代码里复制出来的配置就很容易带进这个逗号。修复只需删掉最后一个逗号;如果这份数据本来就该留在 JS 文件里,改用 JSON5 转换 比硬套 JSON 更合适。
是否可以把带注释的配置文件直接当 JSON 解析?
不行,RFC 8259 的语法里没有注释位置,直接解析会报错。要么删掉注释,要么改存 .jsonc / .json5 并使用支持注释的解析器。各种注释方案的取舍见 JSON 注释怎么写。