# JSON 和 YAML 有什么区别：语法、坑和选型建议

> 比较 JSON 与 YAML 的语法、注释、锚点、类型推断和解析安全，说明 API、配置、CI 与 Kubernetes 场景下该如何选择。

**快速答案:** JSON 语法更严格、解析更简单，适合 API 和机器交换；YAML 更适合人读，支持注释、锚点和多文档，常用于配置和 Kubernetes。需要转换时按消费方要求选择格式，转换后必须校验，因为注释、锚点和多文档在 JSON 中没有等价表示。

## 核心要点

- JSON 和 YAML 能表达相似的数据，但语法、类型推断和安全模型不同。
- YAML 支持注释、锚点和多文档，JSON 不支持。
- YAML 的隐式类型会把未加引号的 yes、no、on、off 解析成布尔值。
- 两种格式都应该用真正的解析器，不要用正则或 split 解析。
- 不可信来源的 YAML 要当作代码处理，使用安全加载器并限制大小和深度。

---

JSON 和 YAML 经常同时出现：API 返回 JSON，部署文件用 YAML，JSON Schema 也可能用 YAML 保存。它们能表达相似的数据，但在语法、安全和工具链上并不能互换。

这篇文章比较真正影响使用的差异，并给出转换时的检查方法。

真正有用的问题不是“哪个更好”，而是“哪种失败模式你能接受”。JSON 出错通常直接、可预测；YAML 则可能通过隐式类型、缩进和锚点悄悄出错。选型时先看文件由谁编辑、由谁解析，再决定容忍哪种失败。

## 同一份数据的两种写法

```json
{
  "service": "api",
  "replicas": 3,
  "enabled": true,
  "ports": [80, 443]
}
```

```yaml
service: api
replicas: 3
enabled: true
ports:
  - 80
  - 443
```

数据相同，语法不同。YAML 依赖缩进和换行，JSON 依赖花括号、方括号、逗号和引号。

这两段代码能解析出同样的树，前提是每个值都没有歧义。把其中一行改成 `version: 1.20`，YAML 可能交给你数字 `1.2`，而 JSON 里的 `"version": "1.20"` 仍然是字符串。YAML 还支持流式写法，例如 `{service: api, replicas: 3}`，看起来像 JSON，实际仍按 YAML 的引号和类型规则解析。不要因为一段文本长得像 JSON，就假设它按 JSON 解析。

## 影响使用的语法差异

| 特性 | JSON | YAML |
| --- | --- | --- |
| 注释 | 不支持 | 支持 `#` |
| 键的引号 | 必须 | 可选 |
| 尾随逗号 | 不允许 | 不适用 |
| 缩进 | 不敏感 | 敏感 |
| 多文档 | 不支持 | 支持 `---` |
| 锚点和别名 | 不支持 | 支持 `&` 和 `*` |
| 显式类型 | 仅 JSON 类型 | 标签和隐式推断 |
| 重复键 | 解析器行为不一致 | 规范要求唯一，解析器仍可能覆盖 |
| 制表符 | 可作空白 | 不能用于缩进 |
| 解析复杂度 | 较低 | 较高 |

JSON 语法小是优点：更容易解析、校验，也更不容易藏歧义。

重复键值得单独警告。RFC 8259 说键名应当唯一，却没有强制报错；于是一个解析器保留后值，另一个保留前值，第三个直接拒绝。YAML 规范要求键唯一，但很多解析器仍会静默覆盖。两种格式里的重复键都是数据丢失的隐患，只是要等换一个工具才暴露。应在文件进入生产前用 linter 检查。

制表符是另一个安静的区别。JSON 把制表符当普通空白；YAML 禁止用它缩进，但允许它出现在带引号的标量里。一个在制表符宽度为 4 的编辑器里看起来对齐的文件，可能在 CI 里直接解析失败，也可能在有人把制表符替换成空格后悄悄变成另一种结构。

## YAML 有而 JSON 没有的能力

### 注释

```yaml
replicas: 3 # 与 HPA 保持一致
```

JSON 没有注释。转成 JSON 后，解释会消失。如果注释重要，应该把 YAML 作为源文件，把 JSON 作为构建产物。

JSONC 和 JSON5 为 `tsconfig.json`、编辑器设置这类配置文件加了注释，但它们不是标准 JSON。发给只调用 `JSON.parse` 的接口会失败，报错往往指向注释而不是真正的问题。注释也不等于约束：如果注释写着“不要超过 10”，就应该再加一条 schema 约束或测试，因为下一个编辑文件的人未必会读它。

### 锚点和别名

```yaml
defaults: &defaults
  timeout: 30
  retries: 3

production:
  <<: *defaults
  timeout: 60
```

锚点可以减少重复。JSON 没有合并键，转换器必须展开每个别名。生成的 JSON 更大，也不再表达共享结构。

有两个细节经常出问题。第一，`<<` 合并键属于 YAML 1.1 的类型约定，不在 YAML 1.2 核心 schema 里；有的解析器支持，有的要加开关，有的静默忽略。第二，别名在解析阶段就被展开，解析后的树没有“共享同一份数据”的概念。一个大量复用锚点的 10 MB YAML，转换后可能变成 100 MB JSON，恶意构造的别名炸弹还能指数级膨胀。要设置别名展开上限，也不要把锚点当成 schema 或模板引擎的替代品。

### 多文档

```yaml
---
kind: Deployment
---
kind: Service
```

YAML 可以包含多个文档，JSON 文件只能有一个值。转换器必须决定输出数组、包装对象还是直接拒绝。

这几种选择并不等价。数组会丢掉源文件名、序号之类的每文档信息；包装对象改变了客户端预期的结构；只取第一个文档则会静默丢掉其余内容。Kubernetes 清单依赖 `---`，只处理一个文档的转换器就是数据丢失 bug。还要注意块标量里的 `---`：shell 脚本或证书里的一行横线不应开启新文档，解析器如果混淆两者，会把一个值从中间劈开。

## YAML 的类型推断坑

YAML 会把未加引号的标量解析成类型。方便，也容易出错。

```yaml
country: NO
enabled: yes
version: 1.20
```

在 YAML 1.1 中，`NO` 可能变成 `false`，`yes` 变成 `true`，`1.20` 变成数字 `1.2`。这就是著名的“挪威问题”。需要字符串时，加引号：

```yaml
country: "NO"
version: "1.20"
```

YAML 1.2 收窄了布尔推断，但不同解析器的行为并不完全一致。加引号最可移植，也最明确。

坑远不止挪威问题。在 YAML 1.1 里，未加引号的 `12:30` 可能被解析成六十进制整数 750，`012` 变成八进制 10，`1_000` 变成 1000；`.inf`、`.nan`、`~` 和空值会变成特殊数字或 null；有些解析器还会把 `2026-11-09` 转成日期对象，再序列化成 UTC 时间戳，日期可能因此偏移一天。GitHub Actions 里的 `on:` 键是著名案例：按 YAML 1.1 规则它会被当成布尔值 `true`，所以工具和文档都要特殊处理。

实用的规则比“布尔值要加引号”更宽：只要类型重要，就加引号。版本号、电话号码、邮编、账号、日期、时间和看起来像布尔的键，都应该加引号。用声明了 schema 版本的 linter，并在 CI 里固定解析器版本，因为升级一个 YAML 库就可能改变现有文件的解析结果。

## JSON 的限制

JSON 刻意保持很小：没有注释、没有日期类型、没有二进制类型、没有引用。日期是字符串，二进制通常用 Base64，共享结构需要重复写。

这些限制让 JSON 更可预测。解析器不需要猜 `NO` 是字符串还是布尔值。

有两个 JSON 限制需要显式处理。多数解析器用 IEEE 754 双精度浮点数表示数字，超过 2^53 的整数会丢精度；一个 19 位的数据库 ID 在 JavaScript 里解析后可能变成另一个数。这类 ID 应该用字符串传输，或者换支持大整数的解析器。另外，JSON 标准没有流式分隔符，JSON Lines（每行一个对象）只是约定，但它是日志和大批量导出的实用选择，因为消费方可以一次处理一条记录。

JSON 的严格性也是安全优势。`JSON.parse` 不会构造对象、调用函数或解析引用；危险的是应用代码拿到结果之后的行为，例如把不可信的键合并进对象，触发原型污染。

## 转换时的坑

JSON 转 YAML 时：

- 现代库通常保留键顺序，但不要依赖顺序表达语义。
- 看起来像数字或布尔值的字符串，在 YAML 输出中应该加引号。
- 部分库会把大整数输出成科学计数法，需要检查。
- Unicode 通常保留，但转义形式可能变化。

YAML 转 JSON 时：

- 注释会丢失。
- 锚点和别名会被展开。
- 多文档必须合并或显式拒绝。
- 自定义标签可能不被支持。
- 隐式类型可能因转换器的 YAML 规则不同而变化。

值得写进测试的具体故障：YAML 的 `enabled: yes` 转成 JSON 后是 `true`，不是 `"yes"`；未加引号的 `version: 1.20` 变成 `1.2`，版本比较依赖的末尾零丢了；块标量 `|` 保留结尾换行，`>` 会折叠换行，校验和或 PEM 内容可能因此变化；`!!binary` 在不同转换器里可能变成 Base64 字符串、字节数组或直接报错；被解析成本地日期的值转成 UTC 时间戳后，东半球用户的日历日期可能差一天。

转换后一定要校验。JSON 用 [JSON 格式化与校验工具](/tools/json-formatter)，YAML 用 [YAML 格式化与校验工具](/tools/yaml-formatter)。一次性转换可以用 [JSON ↔ YAML 转换工具](/tools/json-yaml)，Kubernetes 清单可以用 [Kubernetes YAML 检查工具](/tools/kubernetes-yaml) 复核后再应用。

## 什么时候用 JSON

- API 请求和响应正文。
- 服务之间的数据交换。
- 浏览器存储和配置。
- 需要小语法、严格解析和快速处理的场景。
- 对歧义零容忍的公开契约。

## 什么时候用 YAML

- 人工维护的配置文件。
- CI/CD 流水线。
- Kubernetes 清单和 Helm values。
- 注释和锚点能显著提高可读性的文件。
- 需要人评审和维护的场景。

YAML 是配置语言，JSON 是数据交换格式。用 YAML 传 API 数据可以工作，但会增加解析复杂度和类型意外，收益很小。

这个建议反过来也成立。用 JSON 维护人工配置同样不舒服：不能写注释、键必须加引号、没有锚点，报错还只给字节偏移而不给行号。文件主要由人每周修改时，YAML 是更好的默认值；主要由程序读写时，JSON 更合适。OpenAPI 是常见的折中：规范常用 YAML 编写、以 JSON 发布，YAML 是源文件，JSON 是生成产物。

## 安全：解析不可信 YAML

在很多语言里，YAML 不只是数据。标签可以构造对象，锚点可以指数级展开。恶意 YAML 可能造成拒绝服务，或实例化预期外的类。

规则：

1. 使用只产生 map、list 和标量的安全加载器。
2. 限制文档大小、嵌套深度和别名展开次数。
3. 不要把不可信 YAML 反序列化成应用对象。
4. 不可信网络输入优先使用 JSON。

JSON 解析器也有资源限制问题，但攻击面更小，因为语法不包含对象构造。

不安全加载器的问题与语言有关，模式却通用：如果加载器允许标签实例化任意类，文档就能决定解析时执行哪段应用代码；来自用户或第三方集成的 YAML 绝不能进入这种加载器。即使使用安全加载器，也要限制资源，因为锚点和深层嵌套可以耗尽内存或调用栈。把解析当作不可信输入处理：限制文档大小、嵌套深度和别名数量，并把解析放在有内存上限的进程里。

JSON 也不是自动安全。解析阶段不会执行代码，但深层嵌套仍可能压爆调用栈，超大文档仍可能耗尽内存。两种格式都要做大小和深度限制；区别在于 YAML 还要防对象构造。

## 实用转换流程

1. 明确事实来源：配置用 YAML，API 数据用 JSON。
2. 单向转换，把结果作为构建产物。
3. 用目标格式的校验器验证输出。
4. 和上一版本做 diff。
5. 注释和锚点保留在源文件，不要求在生成文件中保留。
6. 把转换放进 CI，保证可重复。

[JSON 转 YAML 工具](/tools/json-yaml) 可以处理常见情况。Kubernetes 清单在应用前，用 [Kubernetes YAML 检查工具](/tools/kubernetes-yaml) 复核一次。

在 CI 里让转换可重复：固定转换器版本，提交生成文件，重新生成出现 diff 时让构建失败。生成产物除了能解析，还要按 schema 校验；保留几份覆盖锚点、多文档和带引号标量的 golden file。不要手改生成文件，下一次构建会覆盖它，两个事实来源很快会漂移。

## 一句话结论

| 场景 | 更合适的选择 | 原因 |
| --- | --- | --- |
| 公开 API 正文 | JSON | 语法严格，客户端支持广 |
| 人工维护的部署配置 | YAML | 注释、锚点、diff 可读 |
| 程序生成的配置 | 都可以 | JSON 生成更简单、更安全 |
| 日志和大批量导出 | JSON Lines | 一行一条，可流式处理 |
| 不可信网络输入 | JSON | 没有基于标签的对象构造 |
| 需要评审注释的文件 | YAML | 注释能留在评审里 |

机器交换数据、强调严格性时用 JSON；人类维护配置、需要注释时用 YAML。转换要有明确方向，两边都校验，并把看起来像布尔值或数字的字符串加上引号。

这个建议也有失效的时候。只使用 Kubernetes 的团队，不该为了纯粹而引入 JSON 清单；前端团队已经在用 JSONC 写配置，也不必为了注释迁到 YAML。更常见的问题是双向同步：选一个事实来源，只往一个方向转换，并禁止手工编辑生成产物。

## 常见问题

### YAML 是 JSON 的超集吗？

YAML 1.2 在设计上兼容 JSON，很多 JSON 文档也是合法 YAML。但反过来不成立：YAML 的注释、锚点、标签和多文档在 JSON 中没有直接对应。

### 为什么 YAML 会把 yes 解析成 true？

YAML 1.1 会把未加引号的 yes、no、on、off 解析成布尔值。YAML 1.2 收窄了规则，但很多解析器仍按 1.1 行为处理。需要字符串时应该加引号。

### JSON 可以写注释吗？

标准 JSON 不支持注释。JSON5 和 JSONC 为配置场景增加了注释，但它们不是标准 JSON，不应该发送给只接受标准 JSON 的接口。

### JSON 和 YAML 哪个解析更快？

JSON 通常更快，因为语法更小、解析器更简单。YAML 还要处理缩进、多文档和隐式类型，解析成本更高。高频 API 应优先使用 JSON。

### 解析不可信 YAML 安全吗？

使用会构造任意对象的加载器时不安全。YAML 标签在某些语言中可以实例化应用类，锚点还可能造成指数级展开。应该使用只产生 map、list 和标量的安全加载器，并限制文档大小和嵌套深度。

## 参考资料

- [YAML 1.2 规范](https://yaml.org/spec/1.2.2/)
- [RFC 8259 - JSON 数据交换格式](https://www.rfc-editor.org/rfc/rfc8259)
- [Kubernetes - 配置最佳实践](https://kubernetes.io/docs/concepts/configuration/overview/)
- [YAML 布尔类型](https://yaml.org/type/bool.html)

---

- Canonical: https://www.jsonmind.com/zh-CN/guides/json-vs-yaml
- Locale: zh-CN
- Published: 2026-11-09
- Updated: 2026-11-09
- Tools: json-yaml, yaml-formatter, kubernetes-yaml, json-formatter