你寫了一段看起來完全合理的 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.json、package.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/momoa 或 json5 這類函式庫,它們解析允許尾隨 逗號與註解的 JSON 超集。
尾隨逗號其實被允許的場景:JSONC 與 JSON5
有兩種與 JSON 相鄰的格式刻意允許尾隨逗號(以及其他放寬),值得認識:
- JSONC(含註解的 JSON) —— 被
tsconfig.json、VS Code 的settings.json與許多其他開發者設定使用。JSONC 允許////* */註解 並且 允許尾隨逗號。檔案仍叫.json,但標準JSON.parse()會拒絕 —— VS Code 與相關工具自帶 JSONC 感知的 parser。 - JSON5 —— 更完整的超集:註解、尾隨逗號、不加引號的 key、單引號、多行字串、十六進位數字。用
json5npm 套件或 Python 的pyjson5opt-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 複製回去。無需帳號、不上傳、資料不離開你的瀏覽器。
- JSON Fix —— 修復尾隨逗號與其他常見 JSON 錯誤
- 修復「[object Object] is not valid JSON」與其他語法錯誤 —— JSON 語法錯誤的完整指南
- 修復 JSON 裡的尾隨逗號 —— 含壞例與好例的速查指南
- JSON Diff —— 對比修復前後的 JSON,確認修正無誤