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()。对于已经经过模板字符串转义处理的内容,不宜再靠全局正则逐项修补。
备注:内容仅供参考。
