← 全部文章

JSON 中错误的转义字符:原因、示例与修复方法

修复 JSON 中来自 \x 转义、Windows 路径、正则字符串、畸形 \u 以及双重编码 JSON 的「bad escaped character」错误。

SyntaxError: Bad escaped character in JSON at position N 表示解析器在 JSON 字符串里发现了一个反斜杠(\),而它后面的字符不是 JSON 允许的转义字符之一。根据 RFC 8259 第 7 节,JSON 字符串只能转义双引号、反斜杠、正斜杠,以及控制字符转义 bfnrt,或者写作小写 u 后跟恰好四位十六进制数字的 Unicode 转义。

在实际调试中,这个错误通常来自一个被复制粘贴的值:Windows 路径(C:\Users\Ada)、JavaScript 或 shell 转义(\x1b)、正则表达式模式(\d+)、Python 风格的 Unicode 转义(\U0001F600),或者是从日志里被半反转义的字符串。修复方式不是「删掉反斜杠」,而是先确定这个字符串最终应该是什么值,然后写出能表示这个值的 JSON 文本。

本指南以 JavaScript JSON.parse() 的错误措辞为主,但同一规则适用于 Python json.loads()、Go encoding/json、Ruby JSON.parse、PHP json_decode、jq、Postgres jsonb 以及大多数严格的 JSON 解析器。

我遇到的是哪种字符串错误?

30 秒修复法

  1. 跳到报告中的 positionlinecolumn
  2. 往前看一个字符,找那个反斜杠。
  3. 检查反斜杠后面的字符。
  4. 如果反斜杠本身就是数据,把它写成 \\
  5. 如果这个转义属于其他语言(\x\d\U),把它翻译成 JSON 语法。
  6. 如果反斜杠只是从带引号的日志行里复制来的,就多解析一层,而不是用正则去掉它。

例子:

{"path":"C:\Users\Ada\file.json"}
           ^
           U is not valid after a JSON backslash

正确的 JSON 文本:

{
  "path": "C:\\Users\\Ada\\file.json"
}

解析后,应用程序里实际的值仍然是:

C:\Users\Ada\file.json

双反斜杠只存在于 JSON 文本里。

错误长什么样

不同引擎的措辞略有差别:

// V8: Chrome, Node.js, Edge
SyntaxError: Bad escaped character in JSON at position 12

// Firefox
SyntaxError: JSON.parse: bad escaped character at line 1 column 13 of the JSON data

// Safari
SyntaxError: JSON Parse error: Invalid escape character \x

V8 的 position 通常指向反斜杠之后的字符,而不是反斜杠本身。在下面这段坏掉的 JSON 里,报告的字符是 \Users 里的 U

{"path":"C:\Users\Ada\file.json"}
           ^^
           \U is the bad escape

所以当消息说 position 12 时,要检查 position 12 前后一小段窗口。那个坏字符是有用的,但它前面的反斜杠才解释了 bug 的原因。

JSON 允许的全部转义

在 JSON 字符串里,反斜杠只能引出下列这些转义:

JSON 转义 解析后的字符 备注
\" " JSON 字符串内的双引号必须转义
\\ \ 字面反斜杠必须转义
\/ / 可选;/ 不转义也合法
\b 退格 U+0008
\f 换页 U+000C;这就是 \file 在 Windows 路径里危险的原因
\n 换行 U+000A
\r 回车 U+000D
\t 制表符 U+0009
\uXXXX Unicode 码元 小写 u 后恰好四位十六进制

其他所有形式都是无效 JSON:\x\'\d\s\w\0\v\e\U\u{1F600}\N{...}\cA,以及像 \u12 这样的短 Unicode 转义。

快速修复对照表

如果你已经清楚最终值应该是什么,就用这张表。

坏掉的 JSON 文本 为什么失败 合法的 JSON 文本
{ "path": "C:\Users\Ada\file.json" } \U\A 无效;\f 虽然合法,但会变成换页符,不是路径分隔符。 { "path": "C:\\Users\\Ada\\file.json" }
{ "path": "C:/Users/Ada/file.json" } 这不会失败。正斜杠不需要转义。 如果接收方接受正斜杠,就保留原样。
{ "color": "\x1b[32mOK\x1b[0m" } JSON 没有 \xNN 转义。 { "color": "\u001b[32mOK\u001b[0m" }
{ "name": "O\'Brien" } JSON 字符串里的单引号不需要转义。 { "name": "O'Brien" }
{ "pattern": "^\d{4}-\d{2}-\d{2}$" } \d 是正则转义,不是 JSON 转义。 { "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }
{ "char": "\u12" } \u 后面必须跟正好 4 位十六进制数字。 { "char": "\u0012" }
{ "emoji": "\u{1F600}" } JavaScript 源代码字符串支持这种写法,JSON 不支持。 { "emoji": "😀" }{ "emoji": "\uD83D\uDE00" }

有一个别扭的细节值得单独警告:\f 是合法的 JSON 转义。如果 Windows 路径里包含 \file,解析器会把它变成一个换页符加上 ile。解析可能会成功,但路径值已经被损坏了。这就是为什么盲目「修复」路径字符串是有风险的。

原因 1:Windows 路径被复制进 JSON

Windows 路径看起来无害,因为人眼把反斜杠读成路径分隔符:

{ "downloadDir": "C:\Users\Ada\Downloads" }

JSON 把反斜杠读成一个转义序列的开头。它看到 \U,然后就停下了,因为大写 U 不是一个 JSON 转义。

在 JSON 里写双反斜杠:

{
  "downloadDir": "C:\\Users\\Ada\\Downloads"
}

或者,如果接收程序接受正斜杠,就用正斜杠:

{
  "downloadDir": "C:/Users/Ada/Downloads"
}

对配置文件来说,正斜杠通常更不容易出错。对严格的仅 Windows 值来说,双反斜杠是可移植的 JSON 表示法。

原因 2:把 JavaScript 源字符串和 JSON 文本混为一谈

这里是网上很多示例意外把人搞糊涂的地方。有两层:

  • JavaScript 源字符串语法
  • 这个 JavaScript 字符串里面的 JSON 文本语法

下面这段 JavaScript 源代码是合法的:

const raw = '{"path":"C:\\Users\\Ada"}';
JSON.parse(raw);

但真正到达解析器的 JSON 文本是:

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

如果你想在 JavaScript 里测试一段坏掉的 JSON 样本,又不希望 JavaScript 自己先吃掉反斜杠,就用 String.raw

const broken = String.raw`{"path":"C:\Users\Ada"}`;
JSON.parse(broken);

这会抛出 Bad escaped character,因为 JSON.parse() 收到的是真正坏掉的 JSON 文本。

阅读堆栈时用这个心智模型:如果 JSON 来自 .json 文件、HTTP 响应体、localStorage 值或数据库字符串,就修 JSON 文本本身。如果 JSON 在一个 JavaScript 源字符串里,可能需要一层给 JavaScript 的转义,再一层给 JSON 的转义。

原因 3:借用其他语言的转义

JSON 接受 \n\t,但不接受许多在别的编程语言里很常见的转义:

{ "code": "\x1b[0m", "name": "O\'Brien" }

合法 JSON:

{
  "code": "\u001b[0m",
  "name": "O'Brien"
}

常见的「假朋友」:

转义 在这些语言里合法 JSON 修复方式
\x1b JavaScript、Python、许多 shell \u001b
\' JavaScript/Python 的单引号字符串 ',不加反斜杠
\0 JavaScript/Python 的 NUL 简写 \u0000
\v JavaScript 的垂直制表符 \u000b
\U0001F600 Python 的 Unicode 转义 字面 UTF-8 emoji 或代理对
\u{1F600} JavaScript 的 Unicode 码点转义 字面 UTF-8 emoji 或代理对

如果生产方是你自己的代码,不要一个一个手工翻译。构造正常的对象,让语言自带的 JSON 序列化器写出合法的 JSON。

原因 4:正则模式存到 JSON 配置里

正则有它自己的转义语言。JSON 字符串有另一套独立的转义语言。正则的反斜杠必须先熬过 JSON 解析,才能到达正则引擎。

坏掉的 JSON 配置:

{ "datePattern": "^\d{4}-\d{2}-\d{2}$" }

合法的 JSON 配置:

{
  "datePattern": "^\\d{4}-\\d{2}-\\d{2}$"
}

JSON 解析之后,应用程序看到的字符串是:

^\d{4}-\d{2}-\d{2}$

只有到这一步,它才应该变成一个正则:

const config = JSON.parse('{"datePattern":"^\\\\d{4}-\\\\d{2}-\\\\d{2}$"}');
const re = new RegExp(config.datePattern);

同样的规则适用于 \s\w\b、命名分组、后行断言示例,以及替换字符串。如果这个反斜杠是留给后面的解析器用的,就在 JSON 里把它写成双份。

原因 5:畸形的 Unicode 转义

JSON 的 Unicode 转义是定长的:

{ "char": "\u12" }

合法 JSON:

{
  "char": "\u0012"
}

u 必须小写,后面必须跟恰好四位十六进制数字:0-9a-fA-F

下面这些都不是 JSON 的 Unicode 转义:

"\u{2028}"   // JavaScript 源代码风格,不是 JSON
"\U00002028" // Python 风格,不是 JSON
"\u20G0"     // G 不是十六进制数字

基本多文种平面之外的字符,比如许多 emoji 和一些数学符号,可以直接以 UTF-8 形式存进 JSON 里:

{
  "emoji": "😀"
}

如果要转义,则表示为 UTF-16 代理对:

{
  "emoji": "\uD83D\uDE00"
}

避免出现孤立代理,比如没有配对低位代理的 \uD83D。有些解析器把它们当作码元接受,但对格式良好的 Unicode 有要求的下游系统可能会拒绝。

原因 6:手工拼接的 JSON 字符串

这是这个 bug 的生产版本:

// Unsafe: userInput may contain backslashes, quotes, or newlines.
const payload = '{"message":"' + userInput + '"}';

如果 userInputC:\Users\Ada,输出的文本就是无效 JSON。如果它含有 ",JSON 会以另一种方式坏掉。如果它含有原始的换行,你可能会得到一个 bad control character 错误。

用序列化器:

const payload = JSON.stringify({
  message: userInput,
  path: 'C:\\Users\\Ada\\file.json',
  code: '\x1b[32mOK\x1b[0m',
});

JSON.stringify() 会处理 JSON 特有的转义。结果是合法的 JSON 文本:

{
  "message": "...",
  "path": "C:\\Users\\Ada\\file.json",
  "code": "\u001b[32mOK\u001b[0m"
}

同样的原则适用于其他语言:

import json

payload = json.dumps({
    "path": r"C:\Users\Ada\file.json",
    "pattern": r"^\d+$",
})
body, err := json.Marshal(map[string]string{
    "path": `C:\Users\Ada\file.json`,
    "pattern": `^\d+$`,
})

如果你正在修的是生产方,这才是真正的修复。在下游给无效 JSON 打补丁,只会掩盖坏文本的产生位置。

如何定位坏掉的转义

对于粘贴进来的 JSON,下面这个小助手可以让 V8 的 position 附近更容易看清楚:

function showJsonParseContext(raw) {
  try {
    JSON.parse(raw);
    console.log('Valid JSON');
  } catch (error) {
    const message = String(error.message);
    const match = message.match(/position (\d+)/);

    if (!match) {
      console.log(message);
      return;
    }

    const pos = Number(match[1]);
    const start = Math.max(0, pos - 24);
    const end = Math.min(raw.length, pos + 24);
    const excerpt = raw.slice(start, end);

    console.log(message);
    console.log(JSON.stringify(excerpt));
    console.log(' '.repeat(pos - start) + '^');
  }
}

const raw = String.raw`{"path":"C:\Users\Ada\file.json"}`;
showJsonParseContext(raw);

JSON.stringify(excerpt) 是特意用的。它会把反斜杠和控制字符显示成可见的转义,这在 bug 是不可见的空白或过度转义时正是你需要的。

对于 Firefox 那种 linecolumn 的报错,先跳到那一行,再检查那一行上的字符串字面量。如果确切的列落在反斜杠后面,就把前一个字符也一起看进去。

用修复工具还是直接拒绝这段内容?

以下情况适合用修复工具:

  • 你在清理一段粘贴过来的片段。
  • 你在调试一条日志行。
  • 你在审查 LLM 输出。
  • 你可以肉眼确认修复后的值。
  • 这个值不涉及资金流动、权限、删除或不可逆的状态变更。

以下情况应该拒绝这段内容,去修生产方:

  • JSON 来自 API 契约。
  • 这个值影响计费、权限、安全或数据删除。
  • 解析器不得不在多种可能含义之间猜测。
  • 一段路径、正则或转义序列可能是合法的,但语义上是错的。

举个例子,修复 C:\Users\Ada\file.json 不只是一个语法操作。\file 里的 \f 是合法转义,所以某个工具可能会解析出一个换页符,而不是保留反斜杠。得由人或生产方代码来决定正确的路径。

本站的 JSON Fix 工具最适合当作浏览器本地的调试助手:粘贴文本,检查输出,然后对修复后的 JSON 做校验。它不应该成为畸形生产载荷的静默摄入层。

如何安全地反转义 JSON

有时候反斜杠没有错,是 JSON 被双重编码了。你可能在日志里看到这样的东西:

{\"name\":\"Ada\",\"path\":\"C:\\\\Users\\\\Ada\"}

不要用 replace(/\\/g, '') 一把梭。那会破坏真正的转义。

一次解析一层合法的 JSON:

// The outer value is a JSON string that contains JSON text.
const wrapped = '"{\\"name\\":\\"Ada\\",\\"path\\":\\"C:\\\\\\\\Users\\\\\\\\Ada\\"}"';

const once = JSON.parse(wrapped);
// once is: {"name":"Ada","path":"C:\\Users\\Ada"}

const data = JSON.parse(once);
// data is: { name: "Ada", path: "C:\\Users\\Ada" }

如果第一次解析就抛出 Bad escaped character,那就不是简单的编码问题。它本身就是无效的 JSON 文本,需要针对性地修复。

预防清单

  • 永远不要把用户字符串拼进 JSON。
  • 使用 JSON.stringify()json.dumps()json.Marshal(),或者你所在平台的 JSON 序列化器。
  • 正则模式存进 JSON 时用双反斜杠。
  • 只要接收方接受,路径就优先用正斜杠。
  • 从日志、shell 和文档里复制来的示例要加引号并测试。
  • 在 CI 里用真正的解析器校验生成的 .json 文件。
  • 在解析器报错位置附近打印一段安全的预览,不要打印整个载荷。
  • 把自动修复视为开发者工作流,而不是生产契约。

常见问题

「Bad escaped character in JSON」是什么意思?

JSON 字符串里的一个反斜杠后面跟了一个 JSON 不允许出现在 \ 之后的字符。合法的转义是 "\/bfnrtuXXXX

JSON 里的 Windows 路径怎么修?

把路径里每个反斜杠都写成 \\,例如 C:\\Users\\Ada\\file.json。如果接收程序接受正斜杠,C:/Users/Ada/file.json 就是合法的 JSON,而且更好读。

我的正则在 JavaScript 里行,为什么在 JSON 里不行?

JSON 解析器先看到这个字符串,然后正则引擎才看到。像 \d 这样的正则转义在 JSON 里必须写成 \\d,这样解析出来的字符串里才仍然是 \d

\x1b 在 JSON 里合法吗?

不合法。\xNN 在 JavaScript、Python 和 shell 示例里很常见,但 JSON 不支持它。用 \u001b 表示 ANSI ESC 字符,或者在序列化日志之前把 ANSI 颜色码去掉。

这和「Bad control character」是同一个错误吗?

不是。「Bad escaped character」是指反斜杠后面的字符无效;「Bad control character」是指字符串里出现了一个原始的控制字节,比如字面的换行、tab、NUL 或 ESC 字节。

JSON 修复工具能自动修复坏的转义吗?

有时可以,用于目标值明显的粘贴片段。不要静默自动修复 API 载荷、安全敏感数据、支付、权限、删除操作,也不要自动修复那些 \f\n\t 可能合法但并非预期的值。

JSON 怎么反转义?

JSON.parse() 一次解析一层。一个被双重编码的值第一次解析后变成一个普通 JSON 字符串,再解析一次才是真正的对象或数组。不要用正则去掉反斜杠,那会破坏合法的转义。

怎样在源代码里预防这个错误?

构造原生值,然后用 JSON.stringify() 或你所用语言里等价的序列化器输出。不要用字符串拼接来组装 JSON。

立刻修复

参考资料

最后审阅于 2026 年 7 月。