最经典的一句报错
SyntaxError: Unexpected token } in JSON at position 5
所有人第一次写 JSON.parse 都被它迎面撞上。报错信息其实信息量很大:它告诉你 第几个字符(position)出问题、坏掉的 token 是什么、以及「这是 JSON 语法层面的错,不是取值层面的错」。本文把四类最常见报错逐一拆开,并给一套能落到手头的排错流程。
报错信息怎么读
JSON.parse(arg) 抛出的统一都是 SyntaxError,但它有两个关键字段:
- position:错误发生的字符下标(0 起);用它可以直接跳到字符串里那个位置看。
- 消息里的 token:如
}, ``,o,1——就是那一位实际读到的字符。
大多数解析器/校验工具会把这两者高亮成一个「箭头」指向出错点,这正是定位的起点。
常见错误一:Unexpected token 'x'(意外字符)
JSON.parse('{"a":1,,}') // Unexpected token ','
JSON.parse("{a:1}") // Unexpected token 'a' —— 键缺引号
JSON.parse("{'a':1}") // Unexpected token ' — 用了单引号
标准化 JSON 的三条「铁律」经常被 JS 户籍惯坏:
- 键必须双引号,不能裸名也不能单引号;
- 注释不允许(
//、/* */都不能); - ** trailing comma 不允许**——见下。
很多服务端返回的是「类 JSON 但 JavaScript 风格」的字符串,这就要在源头改,而不是在 parse 处硬撑。
常见错误二:Unexpected end of JSON input(未闭合/截断)
JSON.parse('{"a":1') // Unexpected end of JSON input
JSON.parse('') // 空串也是同样的错
这通常意味着字符串根本不完整:要么你切了尾巴,要么网络/存储环节把它截断了。凡是报「Unexpected end」且位置在末尾,优先怀疑负载截断,而不是你少写了一个括号。
常见错误三:尾随逗号
{"a":1,} → Unexpected token '}'
[1,2,] → Unexpected token ']'
JS 的对象/数组字面量允许尾随逗号,于是很自然手滑写进 JSON。排查技巧:很多「JSON 格式化」在线工具会自动在对象末尾补一个逗号,改完直接拿去校验就误报;一定要选「去尾随逗号」或不补逗号的选项。
常见错误四:Unexpected token o(重复 parse / 非字符串)
const obj = {name:'k'};
JSON.parse(obj) // Unexpected token o in JSON at position 0 ← [object Object] 的 'o'
JSON.parse('123') // 合法!会是数字,不抛错
传入的必须是一个字符串(或自动转成字符串的合法输入)。传已经解析好的对象,toString 变成 [object Object],于是报 o。
三板斧排错流程
拿到乱成一团的 JSON,别用肉眼盯,按这套来:
- 格式化:丢进 JSON 格式化工具,压缩的 JSON 展成树状/带缩进,报错箭头指哪、上下文是什么,一眼可见;这是最常被跳过却最高效的一步。
- 字符审计:用「查找替换」把
,\n、{\n、[\n等高危模式显式标出来,重点检查尾随逗号、缺引号键、注释残留;也顺手把响应里混入的 HTML/日志前缀揪出来。 - 分段定位:对几百 KB 以上、且报错靠末端的 payload,先取前/后各一段分别 parse,二分缩小问题区间;结合字节偏移确认是不是多字节字符被截断。
为什么「看起来是对的」却 parse 失败
高发的三类错觉:
- 编辑器/浏览器已帮你转了:复制粘贴时自动把中文引号、全角冒号转成了半角,你以为没问题。
- 多了一层序列化:后端
JSON.stringify后再包一层、或前端把字符串再 stringify,解出来是带引号的字符串而不是对象。 - 经过了编码转义:
\uXXXX转义没被正确还原、换行被写成字面换行(JSON 字符串内换行必须\n转义)。
自查
把这段会踩坑的例子丢进 JSON 校验/格式化,快速找出三个问题并修正:
{
name: "k",
"tags": ["a", "b",],
"note": "line break
here"
}
能指出「键缺引号、尾随逗号、字符串内字面换行」三处,并解释每个的报错 token 长什么样,JSON.parse 的报错对你就不再是谜。