先检查原始输入
对实际收到的 API 响应执行 JSON.parse,再考虑修复。先确认 HTTP 状态和 Content-Type:HTML 错误页不是 JSON。不同 JavaScript 引擎的报错文本与位置格式可能不同。
区分 BOM、零宽字符与合法数据
部分编辑器支持保存为带 BOM 的 UTF-8。解码后的字符串若以 U+FEFF 开头,JSON.parse 会报错;仅在输入约定允许时移除开头的这个标记。不要假设所有编辑器都会添加 BOM。
JSON 字符串外的零宽字符不是合法 JSON 空白;引号内的键名或值却可以合法包含这些字符。例如 U+200D 用于连接 Emoji。应定位并提示人工确认,不能全局删除,否则可能改变原始数据。
尾随逗号与字符串内容
闭合括号前的逗号可能是语法错误,但引号内部的逗号与括号必须保留。单引号、注释和未转义控制字符需要分别诊断,不能用全局替换统一处理。
使用识别字符串边界的保守修复
下面的独立 TypeScript 函数跟踪字符串与转义引号,只尝试移除开头的 BOM 和字符串外的尾随逗号。完整候选文本通过 JSON.parse 后才返回修复建议。应用结果前仍需核对差异。
/** Conservative repair: preserve string contents; never strip invisible characters globally. */
export function repairJsonSyntax(input: string): { candidate?: string; trailingCommaFound: boolean } {
const text = input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
let output = "";
let inString = false;
let escaped = false;
let previous = "";
let trailingCommaFound = false;
for (let index = 0; index < text.length; index++) {
const char = text[index];
if (inString) {
output += char;
if (escaped) escaped = false;
else if (char === "\\") escaped = true;
else if (char === '"') { inString = false; previous = '"'; }
continue;
}
if (char === '"') inString = true;
if (char === ",") {
let next = index + 1;
while (next < text.length && /[\x20\t\r\n]/.test(text[next])) next++;
const closes = text[next] === "]" || text[next] === "}";
if (closes && previous && !"[{,:".includes(previous)) {
trailingCommaFound = true;
continue;
}
}
output += char;
if (!/[\x20\t\r\n]/.test(char)) previous = char;
}
// Only offer a repair if the complete candidate is valid JSON.
try {
JSON.parse(output);
return { candidate: output === input ? undefined : output, trailingCommaFound };
} catch {
return { trailingCommaFound };
}
}
验证合法数据没有被修改
测试字符串中的逗号、转义引号、反斜杠、嵌套数组和 Emoji。合法 JSON 应逐字保持不变;[, ]、{,} 等错误不能被改成空容器;数字中夹入不可见字符时必须继续报错。
const original = JSON.stringify({ text: ",}", emoji: "👩💻" });
console.assert(repairJsonSyntax(original).candidate === undefined);
console.assert(repairJsonSyntax("[,]").candidate === undefined);
console.assert(repairJsonSyntax('{"items":[1,2,],}').candidate === '{"items":[1,2]}');
定位剩余错误并复核
使用 /tools/json/json-validator 查看原始错误以及引擎提供的行列信息。无法获取位置时,不应猜测错误位于首字符。修复后还要按接口约定检查键名和值。
HTML 响应、重复解析与数据截断的排查见 /articles/json-unexpected-token;Unicode 转义与代理对见 /articles/unicode-json-escapes。
数据处理与能力边界
工具在浏览器本地解析,不主动上传粘贴内容。生产样本仍应最小化并先移除密钥。JSON.parse 成功仅证明语法有效,不证明数据满足 Schema、键名没有重复或大整数没有精度损失。