PAYATHON 2026

JS JSON.parse() 解析 Github Job API 的有效 JSON 为何报错?

排障赵师傅

结论

把原始 JSON 放进普通的 JavaScript 模板字符串后,JavaScript 会先处理其中的转义字符,改变传给 JSON.parse() 的内容,导致解析报错。

JSON 字符串里的 \n 会变成实际换行符,\" 会变成不带反斜杠的双引号,从而破坏 JSON 语法。

data.charCodeAt(712) 返回 10,说明该位置是换行符 LF(U+000A)。转义处理已经能解释这里的错误,无须归因于字符编码。

为什么在线验证通过,代码却解析失败?

在线验证器收到的是直接粘贴的 JSON 文本,其中的 \n 仍然是反斜杠和字母 n 两个字符。

执行下面的代码时,JavaScript 会先创建字符串,再交给 JSON 解析器:

const data = `{"description":"第一行\n第二行"}`;
JSON.parse(data);

普通模板字符串会把源码中的 \n 转换成实际换行,所以 JSON.parse() 收到的内容相当于:

{"description":"第一行
第二行"}

JSON 允许在对象属性之间、数组元素之间用换行作为空白,但双引号包围的字符串内部不能直接出现未转义的换行符,因此这段内容无法解析。

题目中的 HTML 属性也有转义问题:

{"html":"<a href=\"/jobs\">职位</a>"}

这段 JSON 本身有效。如果直接放进普通模板字符串,JavaScript 会去掉 \" 中的反斜杠,传给 JSON 解析器的内容就变成:

{"html":"<a href="/jobs">职位</a>"}

href 两侧的双引号会被当作 JSON 字符串的边界。所以,即使修复了换行错误,解析仍可能在后面报错。

手动粘贴调试时,使用 String.raw

调试题目中的数据时,可以在模板字符串前加上 String.raw,保留原始 JSON 中的反斜杠转义序列:

const data = String.raw`[{"description":"\u003cp\u003e第一行\n第二行\u003c/p\u003e","how_to_apply":"\u003ca href=\"mailto:jobs@example.com\"\u003e申请\u003c/a\u003e"}]`;

const jobs = JSON.parse(data);

console.log(jobs[0].description);
console.log(jobs[0].how_to_apply);

原来的代码:

var data = `...`;

需要改为用 String.raw 标记模板字符串,并从原始响应重新复制完整 JSON。

这样,\n、\" 和 \u003c 会一直保留到 JSON.parse() 执行时,再由 JSON 解析器处理。解析后的字段值可以正常包含换行、双引号和 HTML 标签。

String.raw 适用于这里的手动调试场景,但仍然遵循模板字符串语法。粘贴的内容如果包含反引号或 ${...},就需要额外处理。JSON 字符串内部如果原本就有实际换行,它也不会自动将其修复为 \n。

通过请求获取数据时,直接解析响应正文

实际使用时,应直接读取接口响应,避免手动把 JSON 嵌入 JavaScript 源码:

async function loadJobs(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }

  return await response.json();
}

url 应指向实际可访问且返回 JSON 的接口。response.json() 会读取并解析响应正文,返回 JavaScript 值,无须再调用 JSON.parse()。

如果需要先查看原始文本,可以改用:

async function loadJobs(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }

  const text = await response.text();
  console.log(text);

  return JSON.parse(text);
}

两种方式选一种即可。同一个响应正文通常只能读取一次,不要先调用 response.text(),再对同一个响应调用 response.json()。

题目没有提供 fetch() 的具体错误信息,因此无法判断此前请求失败的原因。如果报错涉及 CORS、网络连接或响应内容,需要根据具体错误另行排查,修改字符串转义无法解决这些问题。

Unicode 转义和字符位置该怎么看?

\u003c 和 \u003e 都是合法的 JSON Unicode 转义,分别表示 < 和 >。它们不会导致这里的解析错误,也不需要提前解码。

即使普通模板字符串先把它们转换成 < 和 >,这两个字符通常仍可出现在 JSON 字符串内部。题目中直接破坏语法的是实际换行符和失去转义的双引号。

可以用下面的代码检查报错附近的内容:

console.log(data.charCodeAt(712)); // 10 表示 LF
console.log(JSON.stringify(data.slice(690, 735)));

JSON.stringify() 会将实际换行显示为 \n,便于查看。这里需要区分原始文本和处理后的字符串:

  • 原始 JSON 文本中的 \n 是两个字符,字符编码分别为 92 和 110。
  • JavaScript 处理转义后得到的换行符是一个字符,字符编码为 10。

charAt(712) 使用从 0 开始的 UTF-16 索引。转义处理会改变字符串长度,因此运行时字符串中的位置不能直接当作原始响应中的字节位置。

粘贴后触发表单提交的原因,还需要检查剪贴板内容和目标页面的事件处理。仅凭这个现象,无法认定该字符是某种特殊的“提交字符”;返回值 10 已明确表明它是 LF。

为什么正则替换换行仍然没用?

替换换行只能修复部分问题。普通模板字符串还会去掉 \" 中的反斜杠,因此修复换行后,HTML 属性中的双引号仍可能导致解析失败。

全局替换还可能破坏原本合法的 JSON。例如,字符串外部用于排版的实际换行是合法的,如果全部替换成字面量 \n,反而会引入语法错误。

应从原始响应重新获取数据。手动调试时,用 String.raw 保留转义;通过请求读取时,用 response.json(),或用 response.text() 配合 JSON.parse()。对于已经经过模板字符串转义处理的内容,不宜再靠全局正则逐项修补。

备注:内容仅供参考。