FormatArc 的 JSON 格式化結果,顯示移除註解後的 JSONFormatArc 的 JSON 格式化結果,顯示移除註解後的 JSON
作者: FormatArc 編輯部發布日期: 2026-09-02更新日期: 2026-09-02

JSON 註解方式:為什麼不能寫,以及 4 種替代方法

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 InternationalIETF
規定範圍僅語法。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.loadsjson.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-applejq: 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.jsontsconfig.json 裡會被默默接受,因為那些檔案是以 JSONC 解析的。
  • JSON.parsejson.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.jsontsconfig.jsonlaunch.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.parsejson.loads 時,得先經過 JSONC 專用函式庫,或用下面的移除註解方式轉成標準 JSON 再傳入。

方法 2:JSON5 — 正式規範化的超集

JSON5(json5.org在新分頁中開啟)是有正式規範的 JSON 超集,從 ECMAScript 5 借了幾項便利功能:

  • 行註解與跨行註解
  • 物件和陣列的行末逗號
  • 有效識別符的 key 可以省略引號
  • 單引號字串
  • 以行續接的多行字串
  • 十六進位數字、首尾小數點、InfinityNaN

JSON5 檔案的寫法相當寬鬆:

{
  // 暫存環境的功能開關
  features: {
    newDashboard: true,
    legacyNotifications: false,
    rateLimitRps: 0xff,
  },
  welcomeMessage: 'Hello, world',
  /* 行末逗號沒問題 */
}

因為有正式規範,幾乎所有語言都有對應的函式庫。Node.js 有 json5在新分頁中開啟,Python 有 pyjson5,Ruby 有 json5

JSONC 與 JSON5 該選哪個

看起來很像,但特性不同。

JSONCJSON5
正式規範有(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 允許?JSONCJSON5
行註解 //不允許支援支援
跨行註解 /* */不允許支援支援
行末逗號不允許視解析器支援
key 省略引號不允許不支援支援
單引號字串不允許不支援支援

RFC 8259 欄全部是「不允許」,因為 Section 2 的文法沒有任何接受這些功能的 production rule。字串必須用雙引號包起來,key 也是字串,物件或陣列的最後一個值後面不能有元素。JSON5 因為規範明確加了 ECMAScript 5 語法,五種全部支援。JSONC 固定加入兩種註解,但保留嚴格 JSON 的雙引號 key 和字串;行末逗號的支援取決於解析器(JSONC 規範寫的是 MAY,參考實作 jsonc-parserallowTrailingComma 預設關閉),所以那格寫「視解析器」而非「支援」。

在標準 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 用 json5pyjson5

# 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,解析失敗時可以走自動修復流程:

  1. 帶註解的狀態直接貼到 JSON 格式化器
  2. 點錯誤提示旁邊的「自動修復」
  3. 確認要套用的規則清單,按「套用」
  4. 讀取、驗證、複製格式化後的 JSON

自動修復處理的是 // 行註解、/* */ 跨行註解、行末逗號、連續逗號這四種。字串值裡的 //(例如 "https://example.com")不在處理範圍內,所以 URL 不會被破壞。JSON5 的單引號和無引號 key 不在自動修復範圍,遇到這種檔案請先用 JSON5 函式庫解析再貼入。所有處理都在瀏覽器內完成,資料不會離開你的裝置。

FormatArc JSON 格式化器顯示移除註解後的 JSON 結果FormatArc JSON 格式化器顯示移除註解後的 JSON 結果

常見問題

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.parsejson.loadsencoding/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 格式化器 就能自動移除註解和行末逗號,再格式化驗證