← 全部文章

JSON 中錯誤的轉義字元:原因、範例與修復

修復由 \x 轉義、Windows 路徑、正則字串、殘缺的 \u 值與雙重編碼 JSON 所引發的「bad escaped character in JSON」錯誤。

SyntaxError: Bad escaped character in JSON at position N 表示解析器在 JSON 字串裡遇到一個反斜線(\),而後面接的字元不是 JSON 允許的轉義字元。根據 RFC 8259 section 7,JSON 字串只能轉義雙引號、反斜線、正斜線、控制字元轉義 bfnrt,或者以 u 加上正好 4 位十六進位數字寫成的 Unicode 轉義。

實務除錯時,這個錯誤通常來自某個被複製過來的值:Windows 路徑(C:\Users\Ada)、JavaScript 或 shell 的轉義(\x1b)、正則模式(\d+)、Python 風格的 Unicode 轉義(\U0001F600),或者一個從 log 裡半解碼過的字串。修復不是「把反斜線刪掉」,而是先決定最終字串的值應該是什麼,然後寫出能代表那個值的 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. 如果那個反斜線只是從被引號包起來的 log 裡複製出來的,就一層一層地解析,不要用正則去剝除。

範例:

{"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 前後一小段區間。壞掉的字元有用,但真正說明錯在哪裡的是它前面的反斜線。

JSON 允許的所有轉義

在 JSON 字串中,反斜線只能開啟下列這些轉義:

JSON 轉義 解析後的字元 備註
\" " JSON 字串裡的雙引號必須這樣寫
\\ \ 字面上的反斜線必須這樣寫
\/ / 可選;/ 不轉義也合法
\b 倒退 U+0008
\f 換頁 U+000C;這就是 \file 在 Windows 路徑裡很危險的原因
\n 換行 U+000A
\r 回車 U+000D
\t 定位字元 U+0009
\uXXXX Unicode 碼元 小寫 u 後面正好接 4 位十六進位數字

其他一律不是合法的 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 文字。

讀 stack trace 時可以用這個心智模型:如果 JSON 來自 .json 檔案、HTTP body、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 序列化器去產生合法的 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、命名群組、lookbehind 範例,以及替換字串。只要反斜線是留給後面的解析器用的,在 JSON 裡就要把它加倍。

原因 5:畸形的 Unicode 轉義

JSON 的 Unicode 轉義是固定寬度:

{ "char": "\u12" }

合法的 JSON:

{
  "char": "\u0012"
}

u 必須小寫,後面正好接 4 位十六進位數字:0-9a-fA-F

以下這些都不是 JSON 的 Unicode 轉義:

"\u{2028}"   // JavaScript 原始碼風格,不是 JSON
"\U00002028" // Python 風格,不是 JSON
"\u20G0"     // G 不是十六進位數字

BMP(基本多文種平面)以外的字元,例如許多 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) 是刻意的:它會把反斜線和控制字元顯示成可見的轉義,這正是當你面對隱形的空白字元或過度的轉義時所需要的。

對於 Firefox 風格帶 linecolumn 的錯誤,先跳到那一行,然後檢查該行的字串字面值。如果精確的欄位落在反斜線之後,把前一個字元一起讀進來看。

用修復工具還是拒收 payload?

以下情境使用修復工具:

  • 你在整理貼進來的片段。
  • 你在除錯一行 log。
  • 你在檢視 LLM 的輸出。
  • 你能用肉眼確認修復後的值是對的。
  • 這個值不會用於金流、權限、刪除或不可逆的狀態變更。

以下情境要拒收 payload,回頭修生產者端:

  • JSON 來自 API 契約。
  • 這個值會影響計費、權限、安全或資料刪除。
  • 解析器必須在多種可能的意義之間猜測。
  • 一個路徑、正則或轉義序列可能語法合法但語意錯誤。

例如,修復 C:\Users\Ada\file.json 不只是語法操作。\file 裡的 \f 是合法的轉義,因此工具可能會把它解析成一個換頁字元,而不是保留反斜線。這時得由人或生產者端的程式決定原本的路徑到底是什麼。

本站的 JSON Fix 工具最適合當作瀏覽器本地的除錯助手:貼上文字、檢查輸出,再驗證修復後的 JSON。它不應該成為靜默吞下畸形生產 payload 的中介層。

如何安全地反轉義 JSON

有時候反斜線本身沒錯,只是 JSON 被雙重編碼了。你可能在 log 裡看到這個:

{\"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 裡存正則模式時要把反斜線加倍。
  • 若接收端接受,路徑優先使用正斜線。
  • 從 log、shell、文件複製過來的範例要先加引號並測試。
  • 在 CI 用真正的解析器驗證產出的 .json 檔案。
  • 記錄一段解析器位置附近的安全預覽,而不是把整個 payload 丟到 log。
  • 把自動修復當成開發流程,而不是生產契約。

常見問題

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

JSON 字串中的反斜線後面接了 JSON 不允許出現在 \ 之後的字元。合法的轉義是 "\/bfnrt,和 uXXXX

在 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 不支援。ANSI ESC 字元請用 \u001b,或者在序列化 log 前先把 ANSI 顏色碼去掉。

這跟「Bad control character」是同一回事嗎?

不是。「Bad escaped character」表示反斜線後面那個字元不合法。「Bad control character」表示 JSON 字串裡出現了原始控制位元組,例如字面上的換行、tab、NUL 或 ESC。

JSON 修復工具能自動修好壞掉的轉義嗎?

有時候可以,例如貼進來的片段,且原本的值一看就明白。不要對 API payload、資安相關資料、付款、權限、刪除,或那些 \f\n\t 可能合法但並非本意的值,做靜默的自動修復。

怎麼反轉義 JSON?

JSON.parse() 一次解一層。一個被雙重編碼的值,第一次 parse 後會變成正常的 JSON 字串,第二次 parse 後才會變成真正的物件或陣列。不要用正則去剝反斜線,那會破壞合法的轉義。

怎麼在原始碼裡預防這個錯誤?

建立原生的值,再用 JSON.stringify() 或你所在語言對應的序列化器序列化它們。不要用字串串接去組 JSON。

立刻修復

參考資料

最後審閱於 2026 年 7 月。