你遇到的几乎每一个 JSON 错误都是九种之一。解析器的消息很少会告诉你具体是哪一种,因为大多数解析器报告的是它卡住的 token,而不是你犯的错误,而这两者并不相同。下面是每种错误、你可能看到的消息,以及修复它的编辑方法。
如果你想跟着试试,把这些例子粘贴到 JSON 验证器 中。它会指出错误并标记行号。
1. 尾随逗号
{
"name": "ana",
"role": "admin",
}
最后一个属性后面的逗号在 JavaScript 中是合法的,在 JSON 中是非法的。大多数解析器会在右花括号处报告这个问题,在实际逗号的下一行,这就是为什么人们盯着花括号看却找不到问题。
删掉这个逗号。如果你因为手动编辑配置文件而经常遇到这个问题,JSON 修复工具会删除它们并显示每一个被删除的逗号。
2. 单引号
{ 'name': 'ana' }
JSON 只有一种字符串语法,它使用双引号。单引号是 JavaScript 的习惯。修复方法是机械性的,如果文档很大,用工具比手动操作更好,因为字符串中的引号需要转义而不是简单替换。
3. 未加引号的键
{ name: "ana" }
对象的键是字符串,字符串需要引号。JavaScript 对象字面量允许省略引号,这就是这种错误的来源:有人从源文件中复制了一个对象并期望它是 JSON。但它不是。
4. 注释
{
// the account owner
"name": "ana"
}
JSON 不允许注释。Douglas Crockford 故意移除了它们,因为人们用它们来传递解析指令。// 和 /* */ 都是错误。如果注释很重要,把它们保留在源文件中并从中生成 JSON,或者将注释移到 "_comment" 这样的实际字段中。
5. NaN、Infinity 和 undefined
{ "score": NaN, "limit": Infinity }
这些在 JSON 中都不存在。完全没有对应的语法,这就是为什么错误通常显示为意外的裸词。Python 在不加注意地导出浮点数时会写 NaN 和 Infinity,JavaScript 从缺失的属性中产生 undefined。
你必须决定它们应该变成什么,因为没有无损的答案。null 是最常见的选择,像 "NaN" 这样的字符串是另一种。修复工具选择 null,并将更改与标点修复分开列出,因为这改变的是你的数据而不仅仅是格式。
6. 大小写错误的关键字
{ "active": True, "deleted": NULL }
JSON 关键字是小写的:true、false、null。True 和 None 是 Python 的,NULL 是 SQL 和 C 的。解析器说 “unexpected token T” 是在用最没用的方式告诉你真相。
7. 字符串中的换行符
{ "note": "first line
second line" }
JSON 字符串不能跨行。换行必须在字符串内写成 \n。当有人手动将多行文本粘贴到配置文件中时,这种情况很常见,而产生的错误通常指向下一行,而不是未关闭的字符串。
8. JSON 不允许的数字
{ "a": .5, "b": +1, "c": 007, "d": 1_000 }
四条独立的规则。数字需要在小数点前有一位数字,所以 .5 应写为 0.5。不允许前导加号。不允许前导零,这就是拒绝 007 的原因。数字分隔符是语言特性,不是 JSON 的特性。
9. 未闭合的括号
{
"user": {
"name": "ana"
}
解析器到达文件末尾并在那里报告错误,所以消息会说 200 行文档的第 200 行之类的话,而缺失的花括号实际上在第 2 行。这就是值得拥有好工具的错误:本站的验证器报告的是已打开但未关闭的括号的位置,而不是输入的末尾。
两个不是语法错误的错误
两个文档可以完全有效但仍然是错误的。
重复的键可以正常解析。规范允许它,你可能使用的每个解析器都会保留最后一个值并静默丢弃之前的值,因此数据会在没有任何错误的情况下消失:
{ "region": "eu-west-1", "retries": 3, "region": "us-east-1" }
验证器将重复项报告为带有两个行号的警告,并且仍然将文档称为有效的,因为规范就是这样说的。
长整数也可以正常解析,然后在任何东西将其读取为 double 时立即失去精度:
{ "id": 12345678901234567890 }
用典型的 JSON 库读取它会得到 1234567890123567000。这里的工具在 token 流上工作,而不是解析为 JavaScript 值,因此数字在格式化、压缩和比较中保持不变。这也是 diff 工具 不会仅仅因为两个二十位整数坍缩为相同的浮点值就称它们相等的原因。
正确阅读错误消息
两个习惯可以节省人们大部分花费在这方面的时间。
首先,将报告的位置视为解析器放弃的地方,而不是你出错的地方。缺失的逗号会在下一个 token 处报告,未闭合的括号在文件末尾报告,尾随逗号在右花括号处报告。
其次,检查报告位置之前的字符。在绝大多数情况下,实际错误比投诉早一个 token。
如果你不想做这两件事中的任何一件,将文档粘贴到 验证器 中并阅读消息,它会命名错误的结构,而不是引用无法解析的 token。