← 全部文章

JSON 中的尾隨逗號:為何 { "a": 1, } 會丟錯

JSON 物件或陣列最後一項之後多出一個逗號就會觸發 SyntaxError。學習為何 JSON 禁止尾隨逗號、它們從何而來,以及如何移除。

你寫了一段看起來完全合理的 JSON —— 每個 key 都加了引號、每個值都合法 —— JSON.parse() 卻仍然丟出 SyntaxError。元兇幾乎都是尾隨逗號:物件或陣列最後一項後面那個多餘的 ,。本文說明 JSON 為什麼禁止它、它從哪裡溜進來,以及如何清掉。

什麼是尾隨逗號?

尾隨逗號(也叫「懸掛逗號」或「最終逗號」)是出現在清單 最後 一項之後的逗號 —— 在收尾的括號之前後面什麼都沒有。

// 物件裡的尾隨逗號
{
  "name": "Ada",
  "score": 98,   // ← 這個逗號沒有下一項
}

// 陣列裡的尾隨逗號
[1, 2, 3,]
//       ^ 同樣的問題

上面兩個例子傳給 JSON.parse() 都會丟 SyntaxError

JSON 為什麼禁止尾隨逗號?

JSON 語法(定義於 RFC 8259)規定逗號是元素之間的 分隔符 —— 兩邊必須都有值。JSON 物件的形式規則長這樣:

object = "{" [ member *( "," member ) ] "}"
member = string ":" value

逗號後面必須跟另一個 member。若逗號後沒東西,語法就違規。這是刻意的設計選擇:JSON 把簡單與無歧義解析放在開發者便利之前。

JavaScript 走了另一條路。ES5(2009)明確讓陣列字面量裡的尾隨逗號合法,ES2017 又擴展到函式參數。JS 中的尾隨逗號現在是常見風格 —— 很多 linter 甚至強制要求,因為對清單加新項目時能產生更乾淨的 git diff。

JavaScript 允許的與 JSON 允許的之間這道鴻溝,幾乎是你會遇到的每一個尾隨逗號錯誤的源頭。

尾隨逗號從哪裡來

手動編輯的設定檔

tsconfig.jsonpackage.json 或應用設定這類設定檔由人類編輯。新增或刪除一條後,很容易留下一個孤逗號。許多編輯器(VS Code、WebStorm)會默默接受 JSONC(含註解的 JSON),它允許尾隨逗號,所以檔案在編輯器裡看起來沒事,到執行期就壞了。

// 你刪了 "debug": true 卻忘了把逗號移除
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2020",   // ← 現在變成尾隨
  }
}

JavaScript → JSON 序列化

開發者有時用字串內插 JavaScript 值或用 Array.join(',') 手動拼 JSON 字串。若邏輯無條件地在每一項後加逗號,最後一項就會留下尾隨逗號。

// ❌ 樸素的手動序列化
const parts = items.map(item => `"${item.name}": ${item.value}`);
const json = '{' + parts.join(',') + ',}'; // } 前多出一個逗號

// ✅ 使用 JSON.stringify —— 它總是產出合法 JSON
const json = JSON.stringify(Object.fromEntries(items.map(i => [i.name, i.value])));

LLM 與 AI 輸出

語言模型被要求「輸出 JSON」時經常產生尾隨逗號。模型在訓練中見過大量 JavaScript 程式碼(其中尾隨逗號合法),所以複製了這個模式。在解析 AI 產生的 JSON 之前一定要驗證或修復。

從 JavaScript 原始碼複製貼上

從一個 .js.ts 檔案中複製物件字面量,貼進 JSON 環境,是尾隨逗號最常出現的途徑之一。原始碼是合法 JavaScript;目的地只接受 JSON。

如何找到尾隨逗號

錯誤訊息通常指向接近問題的位置,但不一定精準指到逗號本身 —— parser 是撞上意外的 }] 才意識到不對:

SyntaxError: Expected double-quoted property name in JSON at position 58
// 或
SyntaxError: Unexpected token '}', ..."score": 98,}" is not valid JSON

從回報位置往回找。尾隨逗號就是觸發錯誤的那個收尾括號前緊鄰的字元。

大型 JSON 檔可以用正則搜尋:

// 找出物件/陣列中的尾隨逗號
,\s*[}\]]

如何修復

選項 1 —— 手動刪除

找到出問題的物件或陣列的最後一項,刪掉後面的逗號。小檔案這是最快的辦法。

選項 2 —— 用正則替換(小心)

const fixed = raw.replace(/,\s*([}\]])/g, '$1');
JSON.parse(fixed);

對常見情況可行,但脆弱:它會破壞含有字面字元 ,},] 的字串。只有確定輸入裡沒有這種字串時才用它。

選項 3 —— 用正規的修復 parser

修復 parser 理解完整 JSON 文法,只在結構上不合法的位置移除尾隨逗號,不會碰字串內容。生產程式碼或不可信輸入下這是最安全的做法。

選項 4 —— 設定檔改用 JSONC

若尾隨逗號出現在你能控制的設定檔,可以考慮改用支援 JSONC(含註解的 JSON)的 parser。TypeScript 的 tsconfig.json 已使用 JSONC。Node.js 設定檔可選 @humanwhocodes/momoajson5 這類函式庫,它們解析允許尾隨逗號與註解的 JSON 超集。

尾隨逗號其實被允許的場景:JSONC 與 JSON5

有兩種與 JSON 相鄰的格式刻意允許尾隨逗號(以及其他放寬),值得認識:

  • JSONC(含註解的 JSON) —— 被 tsconfig.json、VS Code 的 settings.json 與許多其他開發者設定使用。JSONC 允許 // / /* */ 註解 並且 允許尾隨逗號。檔案仍叫 .json,但標準 JSON.parse() 會拒絕 —— VS Code 與相關工具自帶 JSONC 感知的 parser。
  • JSON5 —— 更完整的超集:註解、尾隨逗號、不加引號的 key、單引號、多行字串、十六進位數字。用 json5 npm 套件或 Python 的 pyjson5 opt-in。適合人手編輯且你能控制 parser 的場合;不要透過 API 送給期待嚴格 JSON 的客戶端。

經驗法則:任何跨過嚴格 JSON parser 的東西(API 回應、請求 body、任意函式庫消費的檔案)一律把尾隨逗號 排除在外,只在消費方知情的 JSONC/JSON5 環境中擁抱它們。

預防尾隨逗號

  • 絕不手動拼 JSON 字串。JSON.stringify() —— 它總是產出符合規範的輸出。
  • 加入 linter 規則。 ESLint 的 jsonc/no-trailing-commas 規則會在儲存 .json 檔時抓出尾隨逗號。
  • 驗證 AI 輸出。 使用語言模型產出的任何東西之前,務必先跑 JSON.parse()(或修復 parser)。
  • 用 Prettier。 Prettier 會重新格式化 JSON 檔,並在儲存時自動移除尾隨逗號。

常見問題

JSON 允許尾隨逗號嗎?

不允許。RFC 8259 的 JSON 文法嚴格把逗號定義為兩個值之間的分隔符,所以一個之後在 }] 前沒有任何東西的逗號是非法的。每個標準 parser —— JSON.parse()、Python 的 json 模組、Go 的 encoding/json —— 都會拒絕。

我要怎麼從 JSON 中移除尾隨逗號?

單一檔案時,手動刪掉最後一項後面的逗號。需要重複或程式化處理時,用 JSON.stringify() 重新產生 JSON、用修復 parser,或用 JSON Fix 之類的工具 —— 它會移除結構性的尾隨逗號而不動字串內容。

為什麼 JavaScript 允許尾隨逗號而 JSON 不允許?

JavaScript 在 ES5(陣列)和 ES2017(函式參數)加入尾隨逗號,作為能產出更乾淨 git diff 的開發者便利特性。JSON 是刻意極簡的交換格式,把無歧義解析放在便利之前,因此從未採納。完整差異見 JSON vs JavaScript 物件

尾隨逗號會引發什麼錯誤?

通常是 SyntaxError: Unexpected token '}'Expected double-quoted property name in JSON,因為 parser 在逗號後期待另一個值,結果撞上收尾括號。這是更廣義的 JSON.parse「Unexpected token」錯誤 的一個變體。

立刻修 —— 無需任何安裝

如果你手上有一段帶尾隨逗號的 JSON 字串、現在就要修,JSON Fix 會自動移除它們,連同其他常見問題一起處理 —— 單引號、不加引號的 key、Python 字面量、JavaScript 註解。把你的 JSON 貼進去、按 Repair & Format,再把乾淨的 JSON 複製回去。無需帳號、不上傳、資料不離開你的瀏覽器。