不喜欢广告? 无广告 今天

TOML 与 YAML 与 JSON — 配置格式按它们会惹你生气的程度排序

更新于

每种配置格式最终都会背叛你。YAML 有缩进地狱和静默的布尔类型转换,JSON 有不支持注释的政策,TOML 有“等等,这是什么语法”的尴尬时刻。以下是每种格式带来的实际代价,以及何时该选择哪种格式。

TOML 与 YAML 与 JSON — 按其令人烦恼的程度排序的配置格式 1
广告 移除?

每个项目最终都会让你选择一种配置格式。YAML无处不在。JSON比你的一些同事还早。TOML是最近出现的,它高举双手说:“其实,我就是为这个设计的。” 三者最终都会背叛你,只是方式不同。

这里是一个直接对比——相同的配置,三种格式——接着是每种格式让你后悔人生选择的具体时间点。

相同的配置,三种方式

一个基本的Web应用配置:名称、端口、调试标志、版本字符串、数据库设置、允许的来源。没有特别复杂的内容。正是在这里,格式差异开始显现。

托米

# App configuration
[app]
name = "my-app"
port = 3000
debug = false
version = "1.2.3"
allowed_origins = ["https://example.com", "https://api.example.com"]

[database]
host = "localhost"
port = 5432
name = "mydb"

YAML

# App configuration
app:
  name: my-app
  port: 3000
  debug: false
  version: "1.2.3"
  allowed_origins:
    - https://example.com
    - https://api.example.com

database:
  host: localhost
  port: 5432
  name: mydb

JSON

{
  "app": {
    "name": "my-app",
    "port": 3000,
    "debug": false,
    "version": "1.2.3",
    "allowed_origins": [
      "https://example.com",
      "https://api.example.com"
    ]
  },
  "database": {
    "host": "localhost",
    "port": 5432,
    "name": "mydb"
  }
}

一览无余

特征托米YAMLJSON
评论✅ 是✅ 是❌ 否
类型推断显式声明激进(通常错误)显式声明
数组= ["a", "b"]- item 或内联["a", "b"]
尾随逗号不适用不适用❌ 非法
深层嵌套的配置变得冗长可读性尚可冗长但明确
规范稳定性TOML 1.0(2021年,稳定版)1.1与1.2解析器混乱稳定
空值支持❌ 不允许空类型✅ 是 (~null)✅ 是 (null)
常用Cargo.toml,pyproject.tomlGitHub Actions,k8s,Dockerpackage.json,tsconfig.json

YAML:在演示中看起来最棒,直到它不再如此

YAML在演示中看起来很棒。一个扁平的配置几乎像散文一样。问题出现在你遇到它的边缘情况时——而到那时,你的配置文件已经成了关键基础设施。

挪威问题

在YAML 1.1——大多数解析器默认使用的版本中——这些值都是布尔值: y, n, yes, no, on, off, true, false因此 country: NO 被解析为 country: false。这就是它被称为 挪威问题 的原因——挪威的国家代码是 NO。PyYAML在v6.0版本(2022年发布)中修复了这个问题。SnakeYAML(被许多Java工具使用)仍未完全解决。在使用配置值中的裸值之前,请检查你的解析器。 noyes 在配置值中使用裸值。

类型推断错误

YAML中未加引号的值会被类型转换。 port: 8080 变成整数。 version: 1.10 变成浮点数 1.1 ——数学上相等,语义上错误。如果你忘记对版本字符串加引号,你可能会花十分钟去想为什么你的应用认为它运行在v1.1而不是v1.10。解决方法很无聊:对所有应该保持字符串的值加引号。但YAML不会强制你这样做,所以它不会。

缩进是关键

YAML中不允许使用制表符——不是建议,而是非法。在一个文件中混合使用两个空格和四个空格的缩进,你就会得到一个解析错误,而错误通常指向错误的行。GitHub Actions在这方面是最锋利的:一个缩进错误的 run: 块在运行时失败,而不是在解析时失败,因为工作流运行器只验证语法,不验证步骤结构。你将从CI作业中得到“意外值”错误,且没有明确指出是哪个步骤出错,你将花费20分钟添加调试输出,才意识到问题只是期望四个空格而用了两个空格。

如果你的YAML变得缩进不一致,那么 YAML格式化工具 会在你开始调试之前对其进行规范化。

TOML:真正考虑过配置格式的格式

Tom Preston-Werner(GitHub联合创始人)创建TOML,是因为他厌倦了写格式不一致的INI风格配置文件和让他感到意外的YAML配置文件。TOML 1.0于2021年1月发布,经过多年的修订。如今,它是Rust项目(Cargo.toml)、Python打包(pyproject.toml)和Hugo站点的标准。规范稳定,解析器一致,类型系统符合预期。

它做得正确的地方

  • 没有意外的类型转换。 version = "1.10" 始终是字符串。 port = 3000 始终是整数。你写的是什么,得到的就是什么。
  • 注释工作方式正如你所期望的(# 到行尾),不像JSON。
  • 扁平到中等嵌套的配置真正可读,不像深度嵌套的JSON。

表数组语法

TOML的主要障碍是它的表数组语法。如果你想有一个对象数组——比如多个数据库连接——语法如下:

[[databases]]
name = "primary"
host = "db1.example.com"

[[databases]]
name = "replica"
host = "db2.example.com"

每一列 [[double bracket]] section是数组中的一个项目。 databases 它有效。它明确无误。但每个第一次打开TOML文件的开发者都会问:“这是INI吗?”——因为它看起来有点像。当你要向从未见过TOML的贡献者进行入职培训时,这种不熟悉性会产生真实成本。

TOML还缺少 null 类型——有意为之。如果你的模式使用null表示“键存在但明确未设置”,你需要以不同方式建模(完全省略该键,或使用占位符值)。而深层嵌套的配置会变得冗长:TOML没有YAML的锚点/别名系统来重用子树,因此如果配置中有重复结构,就会出现大量复制粘贴。

TOML格式化工具 在你试图清理一个随着时间自然增长的TOML文件时非常有用。

JSON:你熟悉的魔鬼

JSON是为数据交换设计的——机器之间通信——而不是为人类编写配置文件。它最终成为配置格式,是因为每种语言都已有JSON解析器,这种便利性胜出。如今,JavaScript项目中大约有40个JSON配置文件,如package.json、tsconfig.json、.eslintrc.json,全部都是手动编辑的。

没有注释。仍然如此。

Douglas Crockford在2012年有意从JSON中移除了注释——他担心开发者会将其用作解析指令(类似于IE的条件注释)。自那以后,互联网每天都抱怨这一点。人们使用的变通方法:

  • JSONC ——带有注释的JSON。VS Code用它来处理 settings.jsonlaunch.json。无法被标准JSON解析器解析。非标准。
  • JSON5 ——增加了注释、尾随逗号、未加引号的键、多行字符串。有规范和独立解析器。Babel用它来处理配置。仍然不是标准JSON。
  • A "_comment" 钥匙 ——一个字符串字段,保存你的注释文本。有效。但看起来很荒谬。会进入你的数据模型。

尾随逗号

也非法。在数组或对象的最后项后添加一个尾随逗号, JSON.parse 会抛出 SyntaxError: Unexpected token } ——告诉你有问题,但不知道错误的逗号在何处。这是人类编写配置文件中最常见的JSON解析错误,发生是因为其他现代语言(JavaScript数组、Python列表、Rust枚举)都允许尾随逗号,而人类手动编写JSON时也带着相同的习惯。

JSON做得正确的地方

类型系统明确且通用。每个语言中的每个JSON解析器都对 true, 1, "1",并且 null 有统一的理解。JSON Schema是三种格式中最成熟的配置验证选项——VS Code用它来验证tsconfig.json和package.json,并提供行内错误高亮。当工具生成你的JSON(webpack、tsc、npm)时,你并不关心可读性——那是 JSON 格式化程序 的作用。

结论:根据上下文选择,而非偏好

使用JSON 当工具生成或消费它时(package.json、tsconfig、AWS配置、GitHub API响应),或当你需要JSON Schema验证时。不要手动编写超过必要的内容来对抗它。缺少注释确实是个问题,但其普及程度和工具支持是难以反驳的。

使用YAML 当配置主要由人类编写且相对扁平时——GitHub Actions工作流、Docker Compose文件、Kubernetes清单。对任何可能被误认为布尔值或数字的值(版本字符串、国家代码、以数字开头的值)都必须加引号。运行一个lint工具。永远不要使用制表符。将类型推断视为一个错误,而不是一个功能。

使用TOML 当你控制格式选择并希望避免意外的类型转换时。它是三者中最诚实的。如果你正在启动一个全新项目,且没有工具强制要求某种格式,TOML在六个月后最不可能让你感到意外。这种不熟悉性是一次性成本;明确性是永久性的。

想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?