JSON、TOML、YAML、CSON:配置檔案格式怎麼選
結論:配置檔案需要註釋、日期與多行文字,這些恰好是 JSON 的短板。本文把 JSON、TOML、YAML、CSON 放進同一張表比較語法與工具支援,給出按情境的選型結論,並說明轉換時哪些資訊一定會遺失。

結論:機器交換一律用 JSON,人手維護的配置按生態選 TOML 或 YAML(Kubernetes、CI 鎖 YAML,結構扁平或要原生日期選 TOML);只有拿不準讀取方是否支援方言時,才退回 JSONC。
JSON 是給機器交換資料用的,配置檔案是給人寫給人看的。把這兩件事混為一談,就會得到一份既不能寫註釋、又不敢刪欄位的
config.json。
一、先分清兩類檔案
資料交換格式和配置格式的目標並不相同,只是經常被同一個人寫在同一台機器上。
| 維度 | 資料交換 | 配置檔案 |
|---|---|---|
| 誰寫 | 程式序列化 | 人手寫或半手工維護 |
| 誰讀 | 對方的程式 | 自家程式 |
| 關心什麼 | 零歧義、體積、解析速度 | 可讀性、可註釋、可 diff |
| 變更頻率 | 每次請求 | 偶爾,且要審查 |
| 出錯代價 | 介面 500 | 服務起不來 |
JSON 在左列幾乎滿分:語法極簡、所有語言都有解析器、沒有註釋也就沒有「註釋與值不一致」的問題。但它的嚴格性在右列全是負擔:不能寫註釋、不能寫尾逗號、不能寫日期、多行文字必須壓成一行加 \n。
二、四位選手與語法能力
- JSON:RFC 8259 與 ECMA-404 定義,只有物件、陣列、字串、數字、布林、
null六種值。它是資料交換的事實標準,也是唯一一個「不用選、預設就用它」的格式。 - TOML:Tom Preston-Werner 發起,v1.0.0 於 2021 年 1 月發布。定位很明確——「語意顯而易見、能無歧義映射到雜湊表」的最小配置格式。
Cargo.toml、pyproject.toml都以它為預設。 - YAML:1.2.2 是目前規範。縮排表達層級,支援註釋、錨點、多文件。Kubernetes 清單、GitHub Actions、Ansible 都離不開它,代價是規範龐大、實作差異多。
- CSON:CoffeeScript Object Notation,用 CoffeeScript 的物件字面量語法寫配置,Atom 編輯器的
config.cson就是它。註釋、無引號鍵名、尾逗號都支援,但生態基本停留在 JavaScript 圈。
2.1 語法能力對照表
| 能力 | JSON | TOML | YAML | CSON |
|---|---|---|---|---|
| 註釋 | ✗ | ✓ # |
✓ # |
✓ # |
| 尾逗號 | ✗ | ✓(陣列、內聯表) | ✓(串流集合) | ✓ |
| 鍵名可省略引號 | ✗ | ✓ | ✓ | ✓ |
| 原生日期時間 | ✗(只能字串) | ✓ 四種日期時間型別 | ✓(1.1 自動解析) | ✗ |
| 多行字串 | ✗(\n 轉義) |
✓ """ 與 ''' |
✓ 區塊純量與折疊純量 | ✓ heredoc |
| 整數範圍 | 規範不限,互通按雙精度 | 64 位有號,必須無損 | 任意精度 + .inf/.nan |
跟隨 JavaScript |
| 巢狀表達 | 大括號與方括號 | 表、陣列表、點號鍵 | 縮排 | 縮排 |
| 縮排敏感 | ✗ | ✗ | ✓ | ✓ |
null |
✓ | ✗ | ✓ | ✓ |
| 工具覆蓋 | 全部語言 | 主流語言 | 全部語言,實作有差異 | 僅 JavaScript 生態 |
這張表裡有兩格值得單獨說:TOML 沒有 null,這是從 JSON 轉過去時最常見的失敗點;YAML 的「原生日期」在 1.1 解析器裡會自動把 2026-10-31 變成日期物件,跨語言傳輸時型別會漂移。
三、註釋:JSON 唯一繞不過去的短板
先給結論:如果這份檔案由人維護、需要解釋「為什麼是這個值」,就不該用標準 JSON。
現成的四條路,按侵入性從低到高:
- 換成 JSONC。標準 JSON 加註釋與尾逗號,VS Code 的
settings.json、tsconfig.json都是它。代價是讀取方必須用jsonc-parser一類帶註釋能力的解析器,用JSON.parse直接讀會失敗。細節見 JSON5 與 JSONC:帶註釋的 JSON。 - 寫旁註檔案。
config.json保持機器可讀,config.md寫清楚每個欄位的含義與取值理由,兩者在同一個目錄、同一次提交裡更新。 - 把說明塞進 Schema。JSON Schema 的
description、$comment關鍵字可以承載欄位說明,但 Schema 通常不隨執行時配置一起下發,讀配置的人未必看得到。 - 換成 TOML 或 YAML。把註釋能力交給格式本身,解析器天然支援。
{
// 固定 8080:前端建置腳本會讀取這個連接埠做代理
"port": 8080,
// 關閉熱更新,避免 CI 上檔案監聽耗盡 inode
"watch": false
}
JSONC 的問題在於它仍是「方言」:檔案副檔名還是 .json,但只有部分工具認。對外傳輸、跨團隊交付時,它和標準 JSON 的邊界必須寫進文件。
四、日期與多行字串:TOML 的主場
TOML 把日期時間提升為一等型別,分四種:帶偏移的日期時間、本地日期時間、本地日期、本地時間。多行字串也有專門的語法,換行原樣保留:
title = "json-toolkit"
created = 2026-10-31T09:00:00+08:00
description = """
面向開發者的 JSON 工具集,
所有處理都在瀏覽器本地完成。
"""
同樣的資訊寫成 JSON,日期只能退化成字串,多行文字必須轉義:
{
"title": "json-toolkit",
"created": "2026-10-31T09:00:00+08:00",
"description": "面向開發者的 JSON 工具集,\n所有處理都在瀏覽器本地完成。"
}
JSON 裡 "created" 只是一個長得像日期的字串,型別資訊由欄位契約承擔:雙方得額外約定「這個欄位一律 RFC 3339」。TOML 則由解析器直接給出日期物件,不需要口頭約定。
YAML 也能寫多行文字,但兩種寫法語意不同:| 保留換行,> 折疊成空格。選錯符號,一段地址或 SQL 就會在解析後變成一行,這種錯誤很難在審查裡看出來。
五、數字:最容易被忽略的差異
JSON 規範沒有數值精度限制,但 RFC 8259 同時提醒:跨實作交換時按 IEEE 754 雙精度處理才安全。JavaScript 裡超過 2^53 - 1 的整數會靜默丟精度:
JSON.parse('{"id":9007199254740993}').id
// 9007199254740992,最後一位被吃掉
TOML 明確要求 64 位有號整數必須無損處理,超出範圍要報錯而不是悄悄截斷;浮點按 binary64,另外支援 inf 與 nan。YAML 1.1 還多一個歷史包袱:1:30 會被解析成 90(六十進位),布林值接受 yes、no、on、off,於是經典的「挪威問題」——國家代碼 NO 變成 false。
處理金額時,無論用哪種格式,都不要依賴浮點數:用整數分、字串,或資料庫的 decimal 型別。格式化與精度問題在 JSON 與 YAML、XML 的互轉邊界 裡有更完整的討論。
六、表結構與巢狀:TOML 的邊界很清楚
TOML 用表表達巢狀,用雙中括號表達陣列表:
[server]
port = 8080
[[routes]]
path = "/parse"
method = "POST"
[[routes]]
path = "/minify"
method = "POST"
它有兩個硬邊界,轉換前必須知道:
- TOML 文件的根是一個表,所以頂層是陣列的 JSON(如
[1, 2, 3])沒有合法的 TOML 表示,只能包一層{"items": [...]}; - TOML 沒有
null,JSON 裡的null欄位要嘛刪除、要嘛換成空字串或哨兵值,語意由呼叫方承擔。
YAML 則相反,巢狀深度不受限制,還能用錨點和合併鍵重複使用片段:
defaults: &defaults
retries: 3
timeoutMs: 5000
server:
<<: *defaults
port: 8080
錨點是 YAML 的獨有能力,也是轉換時的損失點:JSON 沒有引用語法,展開後檔案會變長,且再也認不出「這兩段本來是同一條配置」。
七、按情境選型的結論
| 情境 | 推薦 | 理由 |
|---|---|---|
| 對外 API、訊息佇列、快取 | JSON | 通用性最高,沒有任何協商成本 |
| 應用配置,結構扁平、型別明確 | TOML | 有註釋、有日期、整數無損,解析器行為穩定 |
| Kubernetes、CI、Ansible | YAML | 生態鎖定,換格式等於換工具鏈 |
VS Code、tsconfig.json |
JSONC | 工具原生支援,不需引入新格式 |
| 需要重複使用配置片段 | YAML | 錨點與合併鍵是唯一現成方案 |
| 程式生成、只被程式讀 | JSON(壓縮) | 無需註釋,體積與解析都最優 |
| 歷史 Atom / CoffeeScript 專案 | CSON,但建議遷移 | 生態已邊緣化,遷移成本隨年限上升 |
一條經驗:配置檔案跟著生態走,不要跟著個人偏好走。 用 Rust 就寫 Cargo.toml,部署到 Kubernetes 就寫 YAML,做 VS Code 擴充套件就寫 JSONC;只有在沒有生態約束的新專案裡,才值得討論「哪種更好寫」。
八、轉換邊界:轉過去往往回不來
| 轉換方向 | 一定遺失的資訊 | 建議 |
|---|---|---|
| JSON → TOML | null、頂層陣列、無法表達的鍵名 |
先規整結構,剔除空欄位 |
| TOML → JSON | 註釋、日期型別、inf / nan |
日期統一轉 RFC 3339 字串 |
| YAML → JSON | 註釋、錨點與合併鍵、標籤、多文件 | 先展開錨點,再逐文件轉換 |
| JSON → YAML | 無結構性損失,但日期字串可能被自動解析 | 給日期字串加引號 |
| CSON → JSON | 註釋、CoffeeScript 表達式 | 只做一次性遷移,不雙向同步 |
轉換工具能保證「值對」,但保證不了「註釋還在」。所以不要設計「JSON 是原始檔、YAML 是建置產物」這種雙向流程;要嘛單向生成,要嘛只維護一份原始檔。
九、實作清單
- [ ] 團隊文件裡寫明「哪類檔案用哪種格式」,並列出允許的方言;
- [ ] 人寫的配置優先 TOML 或 YAML,機器交換一律 JSON;
- [ ] 日期欄位在 JSON 裡統一用 RFC 3339 字串,並在 Schema 裡標註格式;
- [ ] 金額與超過
2^53的整數用字串或整數分,不依賴浮點; - [ ] CI 裡對每種格式分別校驗,不要用
JSON.parse校驗 YAML; - [ ] 格式遷移只做單向,遷移後刪除舊檔,避免兩份配置漂移。
JSON 配置檔案怎麼加註釋?
標準 JSON 不支援註釋。可選方案有三種:改用 JSONC(VS Code 生態的方言,需要 jsonc-parser 讀取)、維護一份旁註文件說明欄位含義、或直接換成 TOML / YAML。對外傳輸的檔案仍建議保持標準 JSON,把說明放在文件或 Schema 裡。
TOML 和 YAML 哪個更適合應用配置?
結構扁平、以鍵值為主、需要日期型別時選 TOML,它的語意更確定,解析器差異更小。需要複雜巢狀、多文件、錨點重複使用,或者目標生態本身就是 YAML(Kubernetes、CI)時選 YAML。兩者都能寫註釋,真正的區分點是結構複雜度與生態綁定。
為什麼 YAML 裡 NO 會變成 false?
YAML 1.1 把 yes、no、on、off、y、n 都當作布林值,所以挪威的國家代碼 NO 會被解析成 false。這是「挪威問題」。規避方法有兩種:升級到按 YAML 1.2 解析的實作,或給所有可能被誤判的字串加上引號。
CSON 還值得用嗎?
新專案不建議。CSON 依賴 CoffeeScript 生態,現成工具少,編輯器與 CI 支援都有限,Atom 停止維護後使用情境進一步收縮。如果專案裡已有 CSON 配置,優先安排一次性遷移到 JSON、TOML 或 YAML,而不是繼續新增。