JSON 工具箱

JSON、TOML、YAML、CSON:配置文件格式怎么选

结论:配置文件需要注释、日期与多行文本,这些恰好是 JSON 的短板。本文把 JSON、TOML、YAML、CSON 放进同一张表比较语法与工具支持,给出按场景的选型结论,并说明转换时哪些信息一定会丢。

入门约 9 分钟发布于 2026-10-31更新于 2026-10-31
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。

现成的四条路,按侵入性从低到高:

  1. 换成 JSONC。标准 JSON 加注释与尾逗号,VS Code 的 settings.json、tsconfig.json 都是它。代价是读取方必须用 jsonc-parser 一类带注释能力的解析器,用 JSON.parse 直接读会失败。细节见 JSON5 与 JSONC:带注释的 JSON。
  2. 写旁注文件。config.json 保持机器可读,config.md 写清楚每个字段的含义与取值理由,两者在同一个目录、同一次提交里更新。
  3. 把说明塞进 Schema。JSON Schema 的 description、$comment 关键字可以承载字段说明,但 Schema 通常不随运行时配置一起下发,读配置的人未必看得到。
  4. 换成 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 就会在解析后变成一行,这种错误很难在 review 里看出来。

五、数字:最容易被忽略的差异

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,而不是继续新增。