如何像专家一样验证与调试 JSON
一份坏掉的 JSON 载荷,根因几乎总是确定的——输入里某一个字节错了。难点是把它定位在一个 200 KB 的 API 响应、4 MB 的配置文件,或者一段流式 NDJSON websocket 帧里。本文是我们内部用的工作流——也是客户拿着让他们的流水线崩掉的负载发给我们时,我们跑的同一套操作。
90 秒分诊
把出问题的文本粘到本站的 JSON 格式化工具里。页面底部会报行号与列号。先把这两个数字记住,再去看解析器的错误文本——因为解析器的位置常常差一,或者按码元而不是字符算,否则你会追着一个不存在的鬼魂查一下午。
一旦知道行号与列号,从那点倒推。真正出错的形式几乎只有这三种:
- 未转义字符:字符串里冒出来的裸双引号、字面换行、裸反斜杠。
- 结构错误:缺少闭合大括号、最后一个元素后多出来的逗号、两个元素之间
,,这样的空分隔。 - 编码错误:UTF-8 BOM、被错认为 UTF-8 的 Latin-1 字节、被截断的代理对。
生产中八类错误
我们调查过的每一份「能解析但用不了」的报告都恰好属于下面八类之一,顺序也大致就是真实 bug 报告里出现的频率顺序。
-
尾逗号。
{"a": 1, "b": 2,}——2后的逗号非法。JSON 在数组和对象里都禁止尾逗号;JavaScript 在数组/对象字面量里允许;很多 YAML 导出器输出 JSON 时带尾逗号,因为 YAML 本身允许。修法是一行正则:s/,\s*([}\]])/$1/g。 -
单引号字符串或未引号 key。
{'a': 1}和{a: 1}都非法。JSON 在所有地方都要求双引号——key、字符串值,都一样。修法是统一用双引号;要写一个在字符串内能正确跳过撇号的正则有点烦,请直接用 JSON 格式化工具的树视图快速定位到错误点。 -
注释。JSON 不支持
//或/* */注释。JSON5 支持;如果源文件带注释,要么接受 JSON5(牺牲对严格下游解析器的兼容性),要么在上游用正则剥掉注释。 -
Unicode 转义。
\x41、\e、单独的\都不合法。JSON 只接受九种转义:\"、\\、\/、\b、\f、\n、\r、\t,以及\uXXXX(恰好 4 个十六进制位,不少也不多)。其他一律拒绝。 -
带前导零的数字。
0123非法;0.123合法;"0123"合法(它是字符串)。JSON 禁止整数上的前导零,原因和 C/Java/Python 历史上一样:0123看起来像八进制,而 JSON 没有八进制。 -
NaN、Infinity、undefined。这些根本不属于 JSON。生产者写出裸的
NaNtoken 就是不符合规范。JSON.stringify 拒绝输出它们——它会替换为null,这是文档化的逃生口。 -
编码错误。带 BOM 的 UTF-8 文件、被错标成 UTF-8 的 Latin-1 文件、SQL Server 导出的 Windows-1252 文件、日文财务系统的 Shift-JIS 文件——对严格的 UTF-8 解析器来说全都表现为「非法 JSON 字符」。第一步始终是在上游做编码检测并重新编码,工作流参考 CSV 编码指南。
-
截断的输入。网络超时、部分文件写入、管道破裂,都会让你拿到一段看起来不错但中途突然结束的 JSON。错误报告会显示一个接近缓冲区末尾的位置。修生产者,不要修解析器。
JSON 能解析但程序仍然崩
这才是真实系统中最常见、也最难调试的情况——每个工具都会告诉你「JSON 合法」。问题在语义,不在语法。JSON 比对工具 在这里是合适的选择:把你的负载和已知正常的参考并排粘进去,结构化视图会列出所有不同的路径。
我们一次又一次看到的模式:
- 字符串 vs 数字。API 期待
"42",收到42,反之亦然。JSON 区分不了「用户年龄作字符串」还是「用户年龄作数字」——只有消费方能区分。金额、ID、任何必须精确往返的值编成字符串;计数、尺寸、参与算术的值编成数字。 - null vs 缺失。
{"name": null}和{}在几乎所有消费方里语义都不同。如果你的序列化器默认省略空字段,{"name": null}就变成了{},消费方那侧的obj.name ?? defaultValue就走defaultValue而不是null。二选一、坚持到底;写到文档里;在对侧加测试。 - 大小写敏感。
"Username"vs"username"差一个字符,JSON 没法解决。服务侧做大小写不敏感匹配会在一层隐藏 bug,下一层又冒出来。 - 日期格式漂移。有产方输出 ISO 8601 字符串(
"2026-08-06T12:34:56Z"),有输出 epoch 秒(1754483696),有输出 epoch 毫秒(1754483696000),还有一两个写得很差的产方输出微软 JSON Date 格式("\\/Date(1754483696000)\\/")。两端都把格式写进文档,并在接收端验证。
把工作流建到工具里
只要你维护的系统还在接触 JSON,你就会每周在搜索框里粘贴同样字符两三次。把肌肉记忆建起来:
- 第一站:粘到 JSON 格式化工具。看行号与列号。
- 第二站:解析器接受了但程序崩,把你的负载和已知正常的参考都粘进 JSON 比对工具,看结构化差异。
- 第三站:如果输入来自第三方 API,把原始字节(不是解析后的值)打进日志,下次 bug 再现时就有重放依据。
肌肉记忆一旦立起来,一份坏掉的 JSON 负载就变成 90 秒修复而不是一下午。工具:JSON 格式化工具、JSON 比对工具,以及 JSON 参考指南 里的规范原文。