设备与系统问题

JSON.parse 为何提示 “Unexpected token”?

文本不是合法 JSON 时,JSON.parse 会抛出 Unexpected token。排查单引号、尾随逗号、HTML 响应和报错位置。

快速回答

输入不是合法 JSON 时,JSON.parse() 会抛出 SyntaxError。「Unexpected token」通常表示解析器读到了当前结构不允许的字符。

常见原因包括单引号、尾随逗号、未加引号的属性名、错误的字符串转义、JSON 前后的多余文本,或把 HTML 等非 JSON 正文当 JSON 来解析。

确切报错措辞随 JavaScript 引擎和浏览器而变。不要假定每个运行时都会打印 “Unexpected token X in JSON at position N”。

本文专讲 JSON.parse 的 Unexpected token:含义、如何读 position、正文是 HTML 时怎么办。尾随逗号、引号和 Unexpected end 的更广覆盖见 常见 JSON 错误:尾随逗号、引号与 Unexpected token。

JSON.parse() 里的 “Unexpected token” 是什么意思?

JSON.parse() 要求符合 JSON 文法。当前位置出现非法字符就会抛出 SyntaxError。{“name”:“Alex”,} 不合法:逗号之后对象不能结束。消息文本和 position 仍因运行时而异。

JSON.parse() 会抛出哪种错误?

非法 JSON 一般会抛出 SyntaxError。把 error.name 打到日志里确认类型。不要把完整的 error.message 写死:引擎和版本的措辞不同。

try {
  JSON.parse('{"name":"Alex",}');
} catch (error) {
  console.log(error.name);
  // SyntaxError
}

1. 用单引号代替了双引号

{‘name’:‘Alex’} 不是合法 JSON。{“name”:“Alex”} 才合法。JSON 的字符串和对象成员名使用双引号。像 const user = { name: ‘Alex’ } 这样的 JavaScript 对象字面量可以是合法 JavaScript,却仍不是 JSON 文本。

无效

{'name':'Alex'}

有效

{"name":"Alex"}

2. 尾随逗号

最后一个属性或数组项后面的逗号,在标准 JSON 里不合法。{“name”:“Alex”,“age”:30,} 和 [1, 2, 3,] 都会失败。把它当作 Unexpected token 的一种成因;常见 JSON 错误:尾随逗号、引号与 Unexpected token 对尾随逗号讲得更细。

无效

{
  "name": "Alex",
  "age": 30,
}

有效

{
  "name": "Alex",
  "age": 30
}
[1, 2, 3,]

3. 未加引号的属性名

{ name: “Alex” } 可以是 JavaScript 对象字面量。在 JSON 里,对象成员名是字符串,需要双引号:{“name”:“Alex”}。

无效

{
  name: "Alex"
}

有效

{
  "name": "Alex"
}

4. 非法的字符串转义

在 JSON 文本里,反斜杠开始一段转义。C:\Users\Alex 这种原始路径不合法,因为 \U 不是合法转义。把反斜杠写成两道。不要把宿主语言的字符串转义和 JSON 转义混在一起。更细的说明见 常见 JSON 错误:尾随逗号、引号与 Unexpected token。

无效

{"file":"C:\Users\Alex"}

有效

{"file":"C:\\Users\\Alex"}

5. JSON 前后多出来的文本

JSON.parse 期望恰好一个 JSON 值。在 {“ok”:true} 前面加 Success:,或在后面加 done,会让整段字符串非法。单个 JSON 值周围无意义的空白是允许的。JSON 并不禁止值的前面或后面有空格。

Success:
{"ok":true}
{"ok":true}
done

6. 把 HTML 当成 JSON 来解析

对 HTML 页面调用 response.json() 是常见的 Unexpected token 情形。错误端点、404 或 500 的 HTML、登录跳转、代理正文、兜底页,开头往往是标记。「Unexpected token < 一定是 HTML」说得太满:< 只是线索,不是证明。

fetch('/api/data')
  .then(response => response.json())

“Unexpected token <” 通常意味着什么?

JSON 值通常不以 < 开头。若正文以 或 起头,< 可能是第一个非法字符。本地排查时看 HTTP 状态、Content-Type 和原始正文。引擎措辞仍然会变。

解析之前先检查响应

fetch 的正文只能消费一次。本地排查时先读 text(),看一小段前缀,再对同一字符串做 JSON.parse。不要对同一份响应先调用 response.json() 再调用 response.text()。默认不要把生产环境的敏感载荷打到日志。

const response = await fetch(url);
const text = await response.text();

console.log(response.status);
console.log(response.headers.get("content-type"));
console.log(text.slice(0, 200));

try {
  const data = JSON.parse(text);
  console.log(data);
} catch (error) {
  console.error("Invalid JSON:", error);
}

“at Position 123” 是什么意思?

有的引擎会报告 position、行号或列号。到附近找坏掉的引号、逗号、括号或转义。那个索引是解析器察觉问题的位置;真正原因可能更靠前。它并不保证就是那个出错字符。

Unexpected token 对比 Unexpected end

Unexpected token 表示解析器碰到了它没料到的字符。输入在值写完之前就停住时,可能出现 Unexpected end,例如 {“name”:“Alex” 少了右花括号。确切措辞取决于引擎。常见 JSON 错误:尾随逗号、引号与 Unexpected token 对残缺 JSON 讲得更广。

{"name":"Alex"

JSON 并不等于 JavaScript 对象字面量

JSON 是数据格式。JavaScript 对象字面量是 JavaScript 语法。带未加引号键、单引号和尾随逗号的 JS 对象不是合法 JSON 文本。JSON.parse 不能安全地解析 JavaScript 对象字面量。

const data = {
  name: 'Alex',
  active: true,
};
{
  "name": "Alex",
  "active": true
}

JSON 里能写注释吗?

标准 JSON 不支持注释。对象里的 // 行是非法的。JSON5 及类似扩展是另一种格式。JSON 格式化、校验与对比 用 JSON.parse 校验标准 JSON,不会把 JSON5 当成 JSON。

{
  // user name
  "name": "Alex"
}

undefined、NaN 和 Infinity 是合法 JSON 吗?

不是。JSON 的值只有 object、array、string、number、true、false 和 null。undefined、NaN 和 Infinity 都不算。顶层不必是对象或数组:true、123、“hello” 和 null 都是合法 JSON。

{"a":undefined}
{"a":NaN}
{"a":Infinity}
true
123
"hello"
null

如何按步骤修复 Unexpected token

动手改源头,而不是改报错字符串:

  1. 确认输入确实是 JSON,而不是 HTML、日志或 JavaScript。
  2. 查看原始输入的开头几个字符。
  3. 若来自 API,检查 HTTP 状态和 Content-Type。200 和 application/json 都不能保证正文是合法 JSON。
  4. 用 JSON 解析器校验语法。
  5. 把报告的 position、行号或列号当线索,不要当成铁定的根因。
  6. 检查那一带的引号、逗号、花括号、方括号和转义。
  7. 对照一份已格式化、已校验的副本。
  8. 去修序列化器或端点。不要随机删掉被报告的记号。
别靠随机替换引号来“修好” JSON

把 ' 全局换成 " 会弄坏字符串里的撇号、已经转义的文本,以及非 JSON 来源。要从真正的序列化器产出合法 JSON。不要把 eval 当成 JSON 解析器。

在 JavaScript 里生成 JSON 时请用 JSON.stringify()

优先用 JSON.stringify,而不是手工拼接字符串。它对能序列化的值会放出合法 JSON 文本。它不会原样保留每一种 JavaScript 值:undefined、函数和符号有特殊行为。细节超出本文范围。

const data = {
  name: "Alex",
  active: true
};
const json = JSON.stringify(data);
// {"name":"Alex","active":true}

非法 JSON 对照真正的修法

下面这些对照已用 JSON.parse 核对过。遇到 HTML 时,该修的是响应,不是去改语法。

问题 无效写法 改法
单引号 {'a':1} {"a":1}
尾随逗号 {"a":1,} {"a":1}
未加引号的键 {a:1} {"a":1}
值为 undefined {"a":undefined} {"a":null}
HTML 正文 <html>... 检查端点和原始响应。不要把 HTML 改写成 JSON。

一份简单的 Unexpected token 判断指南

JSON.parse 失败之后,先判断手里到底有没有 JSON,再去改引号。

  • JSON.parse 失败了
  • 来自 API?查看状态、Content-Type 和原始正文
  • 是否以 JSON 开头?若不是,去修来源或端点
  • 若是,校验引号、逗号、花括号、方括号、转义、不受支持的值
  • 把报告的 position 当线索
  • 修好源头,再解析一次
Unexpected token 只是给这个意外命名。文本从哪来,往往才是真正该修的地方。

解析或使用之前先校验 JSON

NEXNARA JSON 格式化、校验与对比 在浏览器里用 JSON.parse 校验 JSON——不是 eval。

可校验,用 2 空格、4 空格或制表符美化,压缩,复制,下载,上传 .json。JSON Diff 覆盖嵌套对象和数组、路径、摘要、过滤和交换。解析之后,同一份数据的美化版和压缩版比较为相等;对象键按名称比较。

大于 5 MB 的文本可能变慢。大于 15 MB 的文本不会在浏览器里处理。

JSON 校验和格式化在你的浏览器里完成。此工具不会把 JSON 发到 NEXNARA 服务器。广告和其他站点功能仍会使用网络。

把失败的字符串贴进 JSON 格式化、校验与对比,先看开头几个字符,再去改引号。

FAQ

为什么 JSON.parse 会说 Unexpected token?

解析器停下的那一点,文本不是合法 JSON。典型原因是引号、逗号、多余文本,或 HTML 这类非 JSON 正文。

JSON 里的 Unexpected token < 是什么意思?

第一个非法字符可能是 <。这常常指向 HTML 页面,但不能证明每次都是 404 或都是 HTML。请查看状态、Content-Type 和原始正文。

怎样找到 JSON 里的错误位置?

若引擎报告了 position、行号或列号,到附近查看。真正的错误可能更靠前。把它当线索,然后再校验。

JSON 能用单引号吗?

不能。标准 JSON 的字符串和成员名使用双引号。

JSON 能有尾随逗号吗?

不能。标准 JSON 不允许最后一个属性或数组项后面再跟逗号。

怎样检查一段字符串是不是合法 JSON?

在 try/catch 里调用 JSON.parse,或把文本贴进 JSON 格式化、校验与对比。若解析抛出 SyntaxError,该字符串就不是合法 JSON。