toolgarden.xyz
EN
JSON 语法错误修复Unexpected tokenJSON 修复

JSON 报错 Unexpected token 怎么办?常见语法错误逐个排查

尾逗号、单引号、缺逗号、数据被截断、拿到 HTML 而不是 JSON……按固定顺序定位错误,并说明哪些能自动修复、哪些必须回到数据源。

ToolGarden 推荐的工具优先在浏览器本地运行,文件和文本不必上传到服务器,适合更注重安全隐私的日常处理。

发布于 2026年7月20日更新于 2026年10月14日约 10 分钟作者 ToolGarden

JSON 解析报错最让人头疼的地方,是错误信息往往只告诉你“某个位置有问题”,而真正的原因经常在更靠前的地方。这篇文章按“先排除非 JSON,再看语法,再看内容”的固定顺序,带你定位 Unexpected token 一类的报错,并明确区分:哪些问题可以用工具一键修好,哪些必须回到数据来源去查。

第一步:先确认你拿到的真的是 JSON

在逐字符找错误之前,先看第一个字符和最后一个字符。这一步能排除大约一半的“奇怪报错”。

现象真实原因处理
第一个字符是 <(如 <!DOCTYPE)服务器返回了 HTML 错误页、登录页或网关页看 HTTP 状态码和响应头 Content-Type,别在 JSON 上浪费时间
第一个字符不可见开头有 BOM(字节顺序标记)用 JSON 修复页面,它会去掉 BOM
以 jQuery123( 或 callback( 开头JSONP,不是 JSON去掉函数包装再解析
整段被引号包住,里面全是 \"JSON 被二次序列化成字符串先用 JSON 转义页面反转义,再格式化
输入是空的请求失败或响应体为空检查请求本身,空输入在格式化页面不会显示报错

第二步:读懂报错位置

在 JSON 格式化页面粘贴非法内容时,显示的是解析器给出的错误。需要了解一个细节:页面先用标准 JSON 解析,失败后回退到 JSON5 解析,两者都失败时给出的是 JSON5 的报错,格式类似“JSON5: invalid character ... at 行:列”。而浏览器控制台、Node.js 里看到的是另一套措辞,比如“Expected double-quoted property name in JSON at position 7”。两种信息的位置口径不同,但都指向同一个区域。

经验是:报错指向的位置不一定是错误本身。缺逗号的错误会指到下一个键的开头引号;数据被截断会指到文本末尾;JSON5 对 None 这类裸词有时会指到第二个字母。看到报错后,请同时检查它前面的一小段。

第三步:用真实样例逐类排查

1. 尾逗号和注释:可以自动修

{"a": 1, "list": [1, 2, 3,],}   // 两处尾逗号

标准 JSON 不允许尾逗号,原生解析会报“Expected double-quoted property name”。这是最常见的手写配置错误。用 JSON 修复页面,输出会去掉所有尾逗号,字符串内部的逗号和斜杠不受影响。注释同理:// 和 /* */ 会被移除。

2. 单引号、裸 key、弯引号:可以自动修

{'name': 'Ada'}      // 单引号
{name: "Ada"}        // key 没有引号
{“name”: “Ada”}      // 从文档里复制出来的智能引号

前两种在宽松解析下会被接受,输出统一为双引号。弯引号(“ ”)是从 Word、聊天软件复制时带进来的,修复页面会先替换成直引号。注意:弯引号替换是全局的,如果你的字符串内容里本来就有弯引号,修复后它们会变成直引号,需要核对。

3. Python 风格的值:可以自动修

{"ok": True, "owner": None, "debug": False}

直接 print(dict) 得到的输出不是 JSON:True、False、None 首字母大写,且通常使用单引号。修复页面会把它们换成 true、false、null。更稳妥的做法是在 Python 里用 json.dumps,而不是复制 print 的结果。

4. 缺逗号:不会自动修,需要手工补

{"a": 1 "b": 2}

原生报错是“Expected , or } after property value”,JSON5 报错为 invalid character 指向第二个键的开头引号。工具不会替你补逗号,因为无法确定你是漏了逗号还是漏了一个键值对。解决办法:根据报错位置,在前一个值后面补上逗号。大文件里建议在编辑器里把光标放到报错的行列上再看。

5. 数据被截断:不会自动修,回到数据源

{"a": [1, 2

报错信息是“invalid end of input”,位置在文本末尾。常见原因有:日志系统限制了单行长度、复制时没选全、接口因超时中断、数据库字段长度不够。给它手动补括号只会得到一份缺失数据却看似合法的 JSON,这种修复是危险的,应当重新获取完整数据。

6. 无效转义:看工具回退行为再决定

{"path": "C:\Users\bob"}

标准 JSON 中反斜杠必须成对出现,\U 是非法转义,原生解析会报“Bad escaped character”。需要警惕的是:宽松的 JSON5 回退会接受它,把 \U 当作普通字符 U,结果得到 CUsers,\b 则变成退格控制符。也就是说,格式化页面显示“成功”,数据却已经悄悄变形。路径里的反斜杠必须写成 \\,并在格式化后核对字符串值。

7. 字符串里的真实换行和 Tab:需要转义

字符串值里不能包含未转义的换行或制表符,原生报错是“Bad control character in string literal”。多行文本要写成 \n。从日志里复制出的长字符串经常踩到这个坑。

8. undefined、NaN、Infinity:不属于 JSON

{"a": undefined} 在任何模式下都无法解析。NaN 和 Infinity 原生 JSON 不接受,宽松回退虽然能读进来,但输出为标准 JSON 时会变成 null,原始数值信息就丢了。这类值应在生成端处理:序列化前把它们替换为 null 或字符串,并在文档里约定含义。

哪些能自动修,哪些不能:一张表

错误类型JSON 修复页面原因 / 备注
注释(// 和 /* */)可以字符串内部的内容保留
尾逗号可以仅在 } 或 ] 之前
单引号、裸 key可以借助宽松解析器,输出为双引号
弯引号、BOM可以全局替换,需核对
True / False / None可以按单词边界替换,字符串内同样的单词也会被换,需核对
缺逗号、缺冒号不可以无法判断作者意图
数据截断、括号不闭合不可以补全等于编造数据
undefined不可以不是合法值,需回到生成端
Schema 不匹配不属于语法用 JSON Schema 验证页面查缺失字段和类型

实战:一份“看起来只有一个错误”的配置

下面这段配置里有三个问题,它们会按顺序被你一个个撞见:

{
  // 服务配置
  'name': 'billing',
  "retries": 3,
  "debug": False,
  "ports": [8080, 8081,],
  "owner": None
}

一次修复后,注释被移除、单引号变成双引号、False 和 None 变成 false 与 null、尾逗号被删除,得到合法 JSON。如果你只做格式化而不做修复,False 会让解析在第 5 行失败,修好它之后才会暴露 None 的问题,这就是“逐个撞错”的体验。所以面对来源不明的文本,先修复再格式化,能一次看到完整结果。

第四步:语法对了,内容还是不对

语法合法之后,常见的第二类问题是内容与预期不符:字段缺失、类型变化、数字变成字符串。此时不要继续在语法上找原因,而是用 JSON Schema 验证页面核对:它会以“路径加原因”的形式列出每一处不符,例如 $.items[0].price 应为 number。对于超过 2 的 53 次方的大整数,JavaScript 会丢失精度,ID 请使用字符串传输。很多“语法错误”其实是数据本身的问题:同一个接口在不同环境返回不同形状,比如测试环境返回数组,生产环境在没有数据时返回 null。把两个环境的响应分别格式化再对比,往往比盯着报错信息更快找到原因。

从源头预防:别让这些错误再出现

反复修同一类错误,说明问题出在生成端。下面这些做法能把绝大多数语法错误挡在数据产生的地方。

场景错误做法正确做法
Python 输出 JSONprint(dict) 后复制结果使用 json.dumps(obj, ensure_ascii=False),得到真正的双引号和 true / null
JavaScript 拼接 JSON用字符串拼接,值里有引号就坏使用 JSON.stringify,由它负责转义
手写配置凭记忆写,没有校验在编辑器里启用 JSON 语法检查,提交前在 CI 里解析一遍
记录日志按长度截断整行日志记录前完整序列化,或把超长字段单独截断而不是截断整个 JSON
HTTP 接口出错时返回 HTML 页面,Content-Type 仍是 JSON错误也返回 JSON,并设置正确的状态码和 Content-Type

另外两个值得养成的习惯:一是消费方在解析之前先检查响应的 Content-Type 和第一个字符,遇到 < 直接报出“收到了 HTML”,而不是抛出一个晦涩的解析错误;二是在解析失败时记录错误位置周围的几十个字符,而不是整个响应,这样既方便排查,也避免把敏感数据整个写进日志。

一次性排查清单

  1. 响应是否以 { 或 [ 开头,Content-Type 是否是 JSON。
  2. 字符串里是否有未转义的换行、制表符或反斜杠。
  3. 是否有尾逗号、注释、单引号、Python 风格的 True / None。
  4. 括号是否成对闭合,文本末尾是否被截断。
  5. 语法通过后,字段和类型是否与约定一致。

什么时候不要依赖在线修复

  • 数据含有敏感信息且你不确定页面行为:先脱敏,或在本机命令行用 jq 处理。
  • 需要保证“一字不差”的审计数据:任何自动修复都可能改变内容,应当回到数据源重新导出。
  • 截断的大文件:补括号没有意义,应排查上游为什么被截断。
  • 需要保留注释的配置:修复输出是标准 JSON,注释会被移除,请另存为新文件而不是覆盖原文件。

常见问题

Q.Unexpected token < in JSON at position 0 是什么意思?

解析器在第一个字符就遇到了 <,几乎可以确定你拿到的是 HTML(错误页、登录页、网关页),而不是 JSON。检查请求地址、状态码和 Content-Type,而不是修改 JSON 内容。

Q.JSON 为什么不能有尾逗号和注释?

标准 JSON 规范刻意保持极简,不包含注释,也不允许最后一个元素后面带逗号。JSONC 和 JSON5 才允许这些写法。需要交给严格解析器时,请先用 JSON 修复页面转成标准 JSON。

Q.格式化页面显示成功,为什么字符串内容变了?

页面在标准解析失败后会回退到宽松的 JSON5 解析。这会放过一些非法转义,例如 \U 被当成普通字符,导致 C:\Users 变成 CUsers。出现反斜杠路径时,请检查格式化后的字符串值,并在源头使用 \\。

Q.缺逗号为什么不能自动补上?

缺逗号可能是漏了逗号,也可能是漏了整个键值对,工具无法判断你的意图。猜错得到的是一份看起来正确的错误数据,比直接报错更危险,所以需要人工确认。

Q.数据被截断时能不能把括号补齐?

语法上补齐就能解析,但缺失的字段已经丢失,业务上是错误数据。应排查截断原因(日志长度限制、超时、复制不完整)并重新获取。

Q.同一个键出现两次,为什么没有报错?

标准解析器允许重复键并保留最后一个值,不会报错。如果怀疑配置被覆盖,需要在原文中搜索重复的键名,或在代码里使用能检测重复键的解析方式。