“Unexpected token in JSON” 是前后端联调最常见的报错。90% 的情况都栽在下面这 7 个坑里,每一个都附修复方案。
1. 尾逗号
{
"a": 1,
"b": 2, ← 这个逗号 JSON 不允许
}
JavaScript 允许,JSON 禁止。处理外部配置文件时想要尾逗号,用 JSON5 或 JSONC(VSCode settings.json、tsconfig.json 都是 JSONC 方言)。
2. 单引号
{'name': 'Alice'} ← 错
{"name": "Alice"} ← 对
JSON 字符串必须双引号,键也必须带双引号。Python 的 str(dict) 默认输出单引号——要用 json.dumps(),不是 str()。
3. 注释
JSON 规范不支持 // 和 /* */。Crockford 的解释是”我看到有人用注释给 parser 塞指令,干脆取消”。
替代约定:加一个 _comment 字段:
{
"_comment": "开发环境配置",
"api": "https://dev.example.com"
}
或改用 JSONC / JSON5 / YAML。
4. 大整数精度丢失
{"orderId": 9007199254740993}
JS 的 JSON.parse 会把它变成 9007199254740992——最后一位丢了。Twitter 早年就栽过这个坑,把推文 ID 改回字符串返回。后端返回超过 2^53 的整数一律转字符串:
{"orderId": "9007199254740993"}
需要算术的场景前端 BigInt("900...") 处理。
5. 特殊字符没转义
换行、Tab、双引号、反斜杠在字符串里必须转义:
{"msg": "line1\nline2"} 换行用 \n
{"msg": "say \"hi\""} 双引号用 \"
{"msg": "path\\to\\file"} 反斜杠用 \\
中文不用转义,直接 "msg": "你好" 合法(前提文件是 UTF-8)。历史上的 \u4f60\u597d 形式是为了躲 ASCII-only 的传输通道,现在已无必要——除非接收方明确要求。
6. UTF-8 BOM
Windows 记事本”另存为 UTF-8”会在文件开头加 3 字节 EF BB BF(BOM)。JSON 规范要求不带 BOM,否则解析器会在第 1 列就报 “Unexpected token ï”。
查和去:
file config.json # 看是否 "UTF-8 with BOM"
sed -i '1s/^\xEF\xBB\xBF//' config.json # 去 BOM
或 VSCode 右下角切 UTF-8 without BOM 重存。
7. NaN / Infinity / undefined
JSON.stringify({a: NaN}) // '{"a":null}'
JSON.stringify({a: Infinity}) // '{"a":null}'
JSON.stringify({a: undefined}) // '{}' ← 字段直接消失
这三个值不是合法 JSON。JSON.stringify 默默改成 null 或丢掉字段——接收端以为”字段不存在”,其实后端算出了 NaN,bug 就这样埋下。
约定:遇到这三种情况显式传 null + errorReason 字段,或干脆传 "NaN" 字符串,让问题浮出水面。
“标准 JSON 还是 JSON 方言”速查
| 格式 | 尾逗号 | 注释 | 单引号 | 无引号键 | 典型场景 |
|---|---|---|---|---|---|
| JSON (RFC 8259) | ✗ | ✗ | ✗ | ✗ | API 传输、数据交换 |
| JSONC | ✗ | ✓ | ✗ | ✗ | VSCode 配置、tsconfig |
| JSON5 | ✓ | ✓ | ✓ | ✓ | 手写配置 |
| YAML | ✓ | ✓ | ✓ | ✓ | K8s、GitHub Actions |
API 传输永远用标准 JSON;配置文件按工具支持度选方言。
排错顺序
- 贴到格式化工具,看报错定位到哪一行
- 检查末尾是否截断(下载不完整 / 字段太长被截)
- 检查开头是否有 BOM 或多余空白
- 搜尾逗号:
grep -n ',\s*[}\]]' file.json - 搜单引号:
grep -n "'" file.json - 大数值转字符串重传
- 确认
NaN / Infinity / undefined的出处
排完这 7 步,剩下不到 5% 的问题基本是字符编码不一致。