把同一份配置用两种格式写出来,差别一目了然:
{
"name": "devtul",
"port": 8080,
"features": ["json", "yaml"],
"database": { "host": "localhost", "ssl": true }
}
name: devtul
port: 8080
features:
- json
- yaml
database:
host: localhost
ssl: true
YAML 少了引号、少了逗号、少了括号,代价是对缩进极度敏感。
JSON 的优势
- 没有歧义。格式规则简单到可以用几百字写完,任何语言的标准库都能解析,不存在"这个解析器行为不一样"的问题。
- 容错性好。压缩成一行、换行、加不加空格,都不影响结果。用版本控制时冲突也容易看清。
- 解析快。几乎每种语言都有原生加速实现。
缺点是:写的时候啰嗦,不能写注释,多行字符串很难处理。
YAML 的优势
- 读起来干净。层级深的配置,YAML 的扫描速度明显更快。
- 支持注释。这对配置文件的维护性影响很大 —— 半年后回来改,能看懂当初为什么这么写。
- 多行字符串。用
|可以保留换行,用>可以折叠换行,写脚本或者证书内容时非常方便。 - 支持锚点与引用。用
&name定义、*name引用,可以避免重复的配置块。
YAML 的坑
YAML 的表达能力是它的优势,也是它的问题来源。
一、缩进错了就是语法错
用空格还是 Tab,缩进几格,都必须全局一致。编辑器把 Tab 显示成 4 格但实际存了 \t,就会出现"看起来对齐、解析却报错"的情况。
二、隐式类型转换
这一条杀伤力最大:
country: NO # 不是字符串 "NO",而是布尔值 false
version: 1.20 # 不是字符串 "1.20",而是数字 1.2
time: 12:30 # 会被解析成 750(六十进制)
yes_no: yes # 布尔值 true
挪威的区号、软件版本号、时间字符串 —— 这些一旦不加引号,就会被静默地转成别的类型。解决办法就是拿不准就加引号。
三、解析器行为不完全统一
YAML 规范相当庞大,不同语言实现的覆盖度不一致。有些边界写法在一个解析器里能过、在另一个里报错。
怎么选
| 场景 | 建议 | 理由 |
|---|---|---|
| API 请求 / 响应 | JSON | 无歧义、解析快、生态一致 |
| K8s / CI 流水线配置 | YAML | 已是事实标准,且需要注释 |
| 数据交换 / 落盘存储 | JSON | 类型明确,不会猜错 |
| 需要人频繁手改的配置 | YAML | 可读性与注释更重要 |
| 有大量重复配置块 | YAML | 锚点引用能显著减少重复 |
一个实用的折中方案:内部存 JSON,给人看的配置用 YAML 并配好校验。很多工具链就是这么做的 —— 构建时把 YAML 转成 JSON 再喂给下游。
两种格式互转可以直接用 JSON ⇄ YAML 转换。转完记得检查一眼数字和布尔值有没有被隐式改变类型,特别是版本号这类字段。