JSON 可以註解嗎?結論先講
不行。標準 JSON(RFC 8259)的語法中完全沒有註解的規則,在 .json 檔中寫入 // 或 /* ... */ 會讓嚴格的解析器直接報「Unexpected token」錯誤。這不是實作的疏失,而是規格層面的限制:Section 2 的 ABNF 文法沒有定義任何接受註解的 production rule。
但 RFC 8259 的 Section 9 寫明:「JSON parser MAY accept non-JSON forms or extensions」(JSON 解析器可以接受非 JSON 形式或擴展)。這一句就是 JSONC 和 JSON5 存在的合法依據。
實務上如果你需要在 JSON 中留下說明,有以下四種方法:
- JSONC — VS Code 採用的「帶註解的 JSON」
- JSON5 — 正式規範化的 JSON 超集,支援註解、行末逗號、鬆散語法
- 在標準 JSON 中放
"_comment": "..."之類的欄位 - 解析前先用程式移除註解
以下逐一說明各種方法的適用場景,以及如何把帶註解的 JSON 轉成標準 JSON。
如果你只需要快速判斷該用哪種:
- 正在編輯 VS Code 或 Microsoft 系的設定檔 → 直接用 JSONC
- 新專案要選設定格式 → 用 JSON5
- 解析器是固定的、不能換 → 用
_comment欄位或移除註解
帶註解的 JSON 不需要先移除再貼。直接貼到 JSON 格式化器,解析錯誤旁邊會出現「自動修復」按鈕。點一下會顯示要套用的規則清單,按「套用」後 // 和 /* */ 就被移除。所有處理都在瀏覽器內完成,資料不會送到外部。
為什麼 JSON 不允許註解
JSON 的設計者 Douglas Crockford 曾公開表示,註解是故意從規格中拿掉的。原因是實際有案例把解析用的指令藏在註解裡面,導致不同解析器之間的互操作性被破壞。結果 JSON 變得非常精簡:
- 小 — 文法一頁就寫完
- 沒有歧義 — 每個值的解讀只有一種
- 可攜 — 任何語言的內建解析器行為都相同
代價就是:你無法在 JSON 檔裡留下「為什麼要這樣設定」的說明。這就是 trade-off,也是很多工具選擇用 JSONC 或 JSON5 這類超集的原因。
ECMA-404 與 RFC 8259:兩者都禁止註解嗎
是的。JSON 有兩份標準——ECMA-404在新分頁中開啟 和 RFC 8259在新分頁中開啟——兩者的文法都不包含 // 或 /* */。兩份標準刻意保持文法一致,但涵蓋的範圍不同。
| ECMA-404(第 2 版,2017) | RFC 8259(Internet Standard 90,2017) | |
|---|---|---|
| 發布方 | Ecma International | IETF |
| 規定範圍 | 僅語法。Section 1 寫明目的只定義有效 JSON 文本的語法 | 語法加上互操作性的語意約束 |
| 文法中有註解 | 沒有 | 沒有 |
| 對不符合輸入的處理 | Section 2:「符合規格的處理器不應接受不符合規格的 JSON 文本」 | Section 9:「JSON 解析器可以接受非 JSON 形式或擴展」 |
| 兩者的關係 | Section 3:兩份規格意圖描述同一語法語言;RFC 8259 的語意約束對本規格不具有規範性 | 用不同形式定義同一文法 |
| RFC 2119 關鍵字 | 不使用 | 使用(MUST / SHOULD / MAY) |
JSONC 和 JSON5 不算違反規格的依據在 RFC 8259 Section 9 那句「MAY」。反之,如果只依據 ECMA-404 實作的解析器,拒絕帶註解的輸入反而是比較符合規格的行為。同樣是「遵循標準」的解析器,行為卻可能不同,原因就在這裡。
各解析器實際會報什麼錯誤
多數解析器的錯誤訊息裡根本不會出現「comment」這個字。它們在第一個出乎預料的 / 就停下來,然後報告它原本期望找到的 token 名稱。結果就是:錯誤訊息很難直接讓你想到是註解造成的問題。
以下檔案在各解析器下實際輸出的錯誤:
{
// server settings
"host": "localhost",
"port": 8080 /* default */
}
| 解析器 | 實際錯誤訊息 |
|---|---|
JSON.parse(Node.js v26.7.0 / V8,Chrome 與 Edge 的引擎) | SyntaxError: Expected property name or '}' in JSON at position 4 (line 2 column 3) |
JSON.parse,註解在值前面 | SyntaxError: Unexpected token '/', "{"port": /* default"... is not valid JSON |
JSON.parse,註解在值後面 | SyntaxError: Expected ',' or '}' after property value in JSON at position 14 (line 1 column 15) |
Python 3.14.6 json.loads | json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 3 (char 4) |
Python json.loads,註解在值前面 | json.decoder.JSONDecodeError: Expecting value: line 1 column 10 (char 9) |
jq 1.7.1-apple | jq: parse error: Invalid numeric literal at line 2, column 5 |
| VS Code(以嚴格 JSON 處理的檔案) | Comments are not permitted in JSON. |
上表除 VS Code 那一行外,其餘六行都是本專案以上述片段實際量測的結果(詳見 scripts/benchmarks/json-comments-parser-errors/)。VS Code 那行是編輯器語言服務在畫面上顯示的文字,無法從命令列再現。
有幾點值得注意:
- 只有 VS Code 直接點出真正的原因。 它的 JSON 語言服務會逐條標出「Comments are not permitted in JSON.」,但僅限於它當成嚴格 JSON 處理的檔案。同樣的註解寫在
settings.json或tsconfig.json裡會被默默接受,因為那些檔案是以 JSONC 解析的。 JSON.parse和json.loads怪的是下一個 token,不是註解本身。「Expected property name」「Expecting value」代表解析器在/那裡停下來了——行號和欄號其實指的是註解的位置,但訊息裡完全沒有提到註解。jq把/誤讀成數字。「Invalid numeric literal」看起來像是資料問題,但在那一行那一欄通常是註解。
如果你看到這些訊息但檔案裡沒有看得見的註解,那就是語法的其他地方出問題了。
方法 1:JSONC — 帶註解的 JSON
JSONC 是 Microsoft 在 VS Code 中採用的非正式擴展,在標準 JSON 基礎上加了兩種註解:
// 行尾註解/* 跨行註解 */
其他部分跟一般 JSON 完全一樣。典型的 JSONC 檔案長這樣:
{
// VS Code 啟動時使用的主題
"workbench.colorTheme": "Default Dark Modern",
/* 編輯器全域設定
對所有語言都生效 */
"editor.tabSize": 2,
"editor.formatOnSave": true
}
VS Code 的 settings.json、tsconfig.json、launch.json,以及大部分 Microsoft 系工具都預設是 JSONC。Deno 的設定檔(deno.json)也採用 JSONC。
如何解析 JSONC
JSONC 無法用內建的 JSON.parse 讀取,需要專用函式庫。
Node.js:
// npm install jsonc-parser
import { parse } from "jsonc-parser";
const data = parse(sourceText);
Python:
# pip install jstyleson
import jstyleson
data = jstyleson.loads(source_text)
VS Code 本身內建 JSONC 解析器,所以在 settings.json 裡寫註解不會出問題。
只想用行註解(//)的時候 — 最小配置
實務上幾乎只用行註解 //,很少需要跨行註解 /* */。如果只是「想留一行設定意圖的備註」,以下是最小配置。
在 VS Code 中,不需要把整個檔案轉成 JSONC,只要把該檔案的語言模式切到「JSON with Comments」,// 就不會顯示紅色波浪線。點狀態列右下角的語言標示(「JSON」),選「JSON with Comments」即可。
但要注意:改語言模式只是消掉編輯器畫面上的錯誤提示。如果實際讀取這個檔案的程式不支援 JSONC,執行時依然會報「Unexpected token」。把帶 // 的 .json 傳給 JSON.parse 或 json.loads 時,得先經過 JSONC 專用函式庫,或用下面的移除註解方式轉成標準 JSON 再傳入。
方法 2:JSON5 — 正式規範化的超集
JSON5(json5.org在新分頁中開啟)是有正式規範的 JSON 超集,從 ECMAScript 5 借了幾項便利功能:
- 行註解與跨行註解
- 物件和陣列的行末逗號
- 有效識別符的 key 可以省略引號
- 單引號字串
- 以行續接的多行字串
- 十六進位數字、首尾小數點、
Infinity與NaN
JSON5 檔案的寫法相當寬鬆:
{
// 暫存環境的功能開關
features: {
newDashboard: true,
legacyNotifications: false,
rateLimitRps: 0xff,
},
welcomeMessage: 'Hello, world',
/* 行末逗號沒問題 */
}
因為有正式規範,幾乎所有語言都有對應的函式庫。Node.js 有 json5在新分頁中開啟,Python 有 pyjson5,Ruby 有 json5。
JSONC 與 JSON5 該選哪個
看起來很像,但特性不同。
| JSONC | JSON5 | |
|---|---|---|
| 正式規範 | 無 | 有(spec.json5.org在新分頁中開啟) |
| 註解 | 支援 | 支援 |
| 行末逗號 | 部分支援 | 支援 |
| key 省略引號 | 不支援 | 支援 |
| 單引號字串 | 不支援 | 支援 |
| 生態系 | Microsoft / VS Code | 獨立,npm 等 |
選擇的判斷準則:
- 正在編輯 Microsoft / VS Code 設定檔 → 已經在用 JSONC,繼續用就好
- 新專案選設定格式 → JSON5 有規範文件可以給工具引用,比較容易維持一致性
- 跟標準 JSON 解析器的互操作性最優先 → 保持純 JSON,用下面的
_comment欄位模式
各實作的支援現況
下表比較五種常見「非標準」功能在嚴格標準與兩種超集中的支援情況。RFC 8259 欄反映 RFC 8259在新分頁中開啟 Section 2 的文法(與 ECMA-404在新分頁中開啟 相同);JSON5 欄反映公開的 JSON5 規範在新分頁中開啟;JSONC 欄反映 VS Code 文件化的 Microsoft 非正式擴展。
| 功能 | RFC 8259 允許? | JSONC | JSON5 |
|---|---|---|---|
行註解 // | 不允許 | 支援 | 支援 |
跨行註解 /* */ | 不允許 | 支援 | 支援 |
| 行末逗號 | 不允許 | 視解析器 | 支援 |
| key 省略引號 | 不允許 | 不支援 | 支援 |
| 單引號字串 | 不允許 | 不支援 | 支援 |
RFC 8259 欄全部是「不允許」,因為 Section 2 的文法沒有任何接受這些功能的 production rule。字串必須用雙引號包起來,key 也是字串,物件或陣列的最後一個值後面不能有元素。JSON5 因為規範明確加了 ECMAScript 5 語法,五種全部支援。JSONC 固定加入兩種註解,但保留嚴格 JSON 的雙引號 key 和字串;行末逗號的支援取決於解析器(JSONC 規範寫的是 MAY,參考實作 jsonc-parser 的 allowTrailingComma 預設關閉),所以那格寫「視解析器」而非「支援」。
在標準 JSON 中保留註解的替代方法
如果第三方服務只接受標準 JSON,你可以換用字串欄位來放說明,不必改解析器。
模式 1:_comment 欄位
{
"_comment": "在上游逾時之前增加重試次數",
"retries": 3,
"timeout_ms": 5000
}
以底線開頭的 key 慣例上會被消費端忽略,大部分程式會把它當成一般資料處理。這是最簡單的替代方法。
模式 2:每個欄位旁邊放一個註解 key
{
"retries": 3,
"retries_comment": "再高就會超過上游逾時",
"timeout_ms": 5000,
"timeout_ms_comment": "對齊負載平衡器的逾時"
}
檔案會比較長,但哪條註解對應哪個欄位一目了然。
模式 3:外層金屬資料區塊
{
"$meta": {
"generated_by": "deploy.sh",
"purpose": "暫存環境的服務設定"
},
"service": {
"port": 8080,
"retries": 3
}
}
在最上層放一個金屬資料物件,本體 payload 保持乾淨,上下文也留得下來。
這三種方法都會把額外資料寫進檔案,所以嚴格解析器照樣能接受。缺點是這些欄位會變成 schema 的一部分,消費端也需要知道它們存在。
package.json 中寫註解
package.json 是 npm 讀取的標準 JSON 檔案,不能寫 // 或 /* */。寫了的話 npm install 會在 JSON.parse 階段失敗。但 npm 會忽略不認識的最上層 key,所以可以套用上面的替代模式。
最常見的做法是用 "//" 這個慣例 key 放說明:
{
"//": "使用私有 registry 的設定,僅內部 CI 有效",
"name": "my-app",
"version": "1.0.0",
"scripts": {
"build": "tsc -p ."
}
}
同一個 key 在同一個物件裡只能用一次,所以要留多條說明就用陣列:
{
"__comments": [
"部署腳本不在這個檔案管理",
"engines 要對齊 CI 的 Node 版本"
],
"name": "my-app",
"engines": { "node": ">=20" }
}
npm 本身會忽略這些 key,但 npm publish 之後這些說明也會跟著包出去。適合內部工具或私有 repo 的 package.json。如果一定要把設定意圖留下來,比較安全的方式是用 JSONC 寫好後再轉成標準 JSON 發布。
移除註解後再交給標準解析器
如果你手上是 JSONC 或 JSON5 檔,要餵給標準 JSON 解析器,有兩種做法:用 JSONC / JSON5 函式庫解析後重新序列化成 JSON,或用正則表達式移除註解。
Node.js 用 json5 函式庫的範例:
// npm install json5
import JSON5 from "json5";
import fs from "node:fs";
const source = fs.readFileSync("config.json5", "utf8");
const data = JSON5.parse(source);
fs.writeFileSync("config.json", JSON.stringify(data, null, 2));
最簡單的正則移除也可以,但要注意:如果字串值裡面有 //,會被誤刪。所以只建議用在你能完全控制內容的檔案上。
const stripped = source
.replace(/\/\/[^\n\r]*/g, "")
.replace(/\/\*[\s\S]*?\*\//g, "");
const data = JSON.parse(stripped);
比較安全的做法是一律走專用解析器。
用 Python 處理 JSONC / JSON5
Python 標準函式庫 json 只能讀嚴格 JSON。把 JSONC 或 JSON5 直接傳給 json.loads 會在註解那行失敗。對策有兩種。
比較輕量的做法是用標準函式庫 re 移除註解再傳入 json.loads:
import json, re
def load_jsonc(text):
text = re.sub(r"//[^\n]*", "", text) # 行註解
text = re.sub(r"/\*.*?\*/", "", text, flags=re.S) # 跨行註解
return json.loads(text)
with open("tsconfig.json", encoding="utf-8") as f:
config = load_jsonc(f.read())
這個正則也會把字串值裡的 // 刪掉,所以不要用在不受你控制的檔案上。
如果要連行末逗號、省略引號都正確讀取,用專用函式庫。JSONC 用 jstyleson,JSON5 用 json5 或 pyjson5:
# pip install jstyleson json5
import jstyleson
import json5
config = jstyleson.load(open("settings.json", encoding="utf-8")) # JSONC
data = json5.load(open("config.json5", encoding="utf-8")) # JSON5
只是讀取設定用正則就夠;如果輸入來源不可信、或可能混有行末逗號和單引號,走專用函式庫比較保險。
用 FormatArc 格式化並驗證結果
轉成標準 JSON 後,貼到 JSON 格式化器 就能確認格式有沒有問題。如果解析器還是報錯(典型的是 Unexpected token /),幾乎一定是還有註解沒清乾淨。
FormatArc 本身用瀏覽器內建的 JSON.parse,解析失敗時可以走自動修復流程:
- 帶註解的狀態直接貼到 JSON 格式化器
- 點錯誤提示旁邊的「自動修復」
- 確認要套用的規則清單,按「套用」
- 讀取、驗證、複製格式化後的 JSON
自動修復處理的是 // 行註解、/* */ 跨行註解、行末逗號、連續逗號這四種。字串值裡的 //(例如 "https://example.com")不在處理範圍內,所以 URL 不會被破壞。JSON5 的單引號和無引號 key 不在自動修復範圍,遇到這種檔案請先用 JSON5 函式庫解析再貼入。所有處理都在瀏覽器內完成,資料不會離開你的裝置。


常見問題
RFC 8259 對 JSON 註解怎麼說?
RFC 8259 全文沒有使用過「comment」這個詞。Section 2 的 JSON 文法(ABNF)定義了 token 之間唯一允許的空白字符只有四種:space、tab、line feed、carriage return(ws = *( %x20 / %x09 / %x0A / %x0D ))。沒有接受 // 或 /* */ 的 production rule,所以嚴格解析器會以「Unexpected token」拒絕。Section 9 則寫明解析器「MAY accept non-JSON forms or extensions」,這是 JSONC 和 JSON5 不構成違規的依據。
.json 檔可以寫 // 或 /* 註解嗎?
如果被嚴格解析器(JSON.parse、json.loads、encoding/json)讀取,就不行。第一個註解處就會報「Unexpected token」。改成 .jsonc 副檔名並用 JSONC 解析器讀取,或轉成 JSON5。
可以只註解 .json 檔中的一行嗎?
不行。JSON 沒有行註解語法,在行首加 // 會讓那一行變成語法錯誤。要暫時停用某個值的話有三種寫法:把整個 key 刪掉留在版本管理歷史中、把 key 改名成 _disabled_xxx 之類的格式暫存、或把檔案切到 JSONC 環境正式註解。
為什麼 VS Code 在 settings.json 寫註解不會壞?
因為 VS Code 把 settings.json 這類檔案當成 JSONC 處理,不是嚴格 JSON。內建解析器接受 // 和 /* */。用 JSON.parse 讀同一個檔案的其他編輯器如果不認識 JSONC,就會失敗。
JSON5 是標準規格嗎?
JSON5 在 spec.json5.org在新分頁中開啟 有公開規範,但不是 IETF 或 ECMA 的正式標準。開發工具圈支援很廣泛,但不能取代 API 或通訊協議中使用的 RFC 8259 JSON。
應該用 _comment 欄位還是直接換 JSON5?
如果檔案會被多種工具讀取、其中有些只接受嚴格 JSON,就保持純 JSON 用 _comment 模式。如果檔案只有你自己控制的程式會讀,JSON5 比較乾淨,真正的註解比 _comment 欄位好維護。
FormatArc 支援 JSONC 或 JSON5 嗎?
JSONC 可以透過自動修復處理。帶註解的狀態直接貼到 JSON 格式化器,從「自動修復」到「套用」走完之後,// 和 /* */ 就被移除,行末逗號和連續逗號也會一併修正。JSON5 的單引號和無引號 key 不在自動修復範圍,這種檔案要先用 JSON5 函式庫解析再貼入。
YAML 呢?
YAML 原生支援 # 註解。如果主要需求就是留說明、而且可以換格式,YAML 對設定檔通常更合適。
TOML 呢?
TOML 用 # 寫註解,專為設定檔設計,key 不用 schema 也能讀。手動編輯的檔案比較適合 TOML;需要跨服務傳送資料的場景不適合,因為 TOML 解析器的普及度不如 JSON。
總結
- 標準 JSON 不支援註解,這是規格層面的刻意設計
- JSONC 允許
//和/* */,VS Code 全域使用 - JSON5 是正式規範化的超集,有註解、行末逗號、寬鬆語法
- 解析器不能換的時候,用
_comment欄位或外層金屬資料區塊 - 帶註解的 JSON 貼到 JSON 格式化器 就能自動移除註解和行末逗號,再格式化驗證