在 FormatArc 的 YAML to JSON 工具中,左侧输入 YAML 配置,右侧显示转换后的 JSON 结果在 FormatArc 的 YAML to JSON 工具中,左侧输入 YAML 配置,右侧显示转换后的 JSON 结果
作者: FormatArc 编辑部发布日期: 2026-09-02更新日期: 2026-09-02

YAML 和 JSON 区别:语法、注释、解析器实测与选型指南

YAML 和 JSON 的区别——先说结论

一句话:API 和机器之间的数据交换用 JSON,人手写的配置文件用 YAML。两种格式描述的是同一套数据模型——对象、数组、字符串、数字、布尔值——但 JSON 严格且冗长,YAML 基于缩进、支持注释、读起来更清爽。

JSONYAML
语法花括号和方括号缩进(空格)
注释不支持支持(#
主要用途REST API、数据交换、存储配置文件(Kubernetes、GitHub Actions、Docker Compose)
可读性面向机器面向人
严格程度严格宽松(隐式类型推断)

核心要点

  • JSON 严格、解析速度快、适合机器处理;YAML 基于缩进、人读起来舒服、能写注释
  • REST API、日志流、高吞吐场景用 JSON;Kubernetes、GitHub Actions、Ansible 这类需要人编辑的配置文件用 YAML
  • YAML 原生支持注释、锚点(引用)、多行字符串、日期类型,JSON 都没有
  • 喂给 LLM 提示词时,YAML 比带缩进的 JSON 省约 20% 令牌(本站实测 -22%)。但如果用压缩 JSON(去掉所有空白),结果反转,JSON 反而更省。JSON 解析更快,面对不可信输入也更安全

这篇文章会依次讲清楚语法差异、同一份数据两种写法的对比、实际场景中的选型标准,以及转换时容易踩的坑。2026 年以来越来越多人关注"喂给 LLM 提示词用哪种格式更省令牌",这里也一并覆盖。

手头有一份配置文件想看两种格式的差异,可以把 YAML 粘贴到 YAML to JSON 转换工具,转出来的 JSON 再丢回去对比,一分钟内就能看出同一份数据在两种格式下的变化。所有处理都在浏览器里完成,数据不会上传到任何服务器。

实际场景中的选型

在看语法之前,先说实际工作中"选哪个"的常见判断:

  • Kubernetes 清单、Helm Chart——YAML
  • Docker Compose 文件——YAML
  • GitHub Actions、GitLab CI、CircleCI 流水线配置——YAML
  • Ansible Playbook——YAML
  • REST API 的请求体和响应体——JSON
  • package.jsoncomposer.json、浏览器与服务端通信——JSON
  • 日志记录、事件流——JSON Lines(JSONL)
  • OpenAPI / Swagger 规范——YAML 或 JSON(人写用 YAML,工具生成 JSON 的居多)

规律很清楚:人读人写的文件用 YAML,程序生成和消费的数据用 JSON。拿不准的时候就记住这一条。

语法对比:同一份数据,两种写法

拿一个简单的 Web 服务器配置,分别用两种格式写一遍。

YAML:

# Web 服务器配置
server:
  host: localhost
  port: 8080
  debug: true
  allowed_origins:
    - https://example.com
    - https://staging.example.com
  headers:
    X-Frame-Options: DENY
    Strict-Transport-Security: "max-age=31536000"

JSON:

{
  "server": {
    "host": "localhost",
    "port": 8080,
    "debug": true,
    "allowed_origins": [
      "https://example.com",
      "https://staging.example.com"
    ],
    "headers": {
      "X-Frame-Options": "DENY",
      "Strict-Transport-Security": "max-age=31536000"
    }
  }
}

值得注意的有两点。第一,YAML 去掉了大部分花括号、引号和逗号,代码更短。数一下上面两段:YAML 232 字符,JSON 291 字符,差约 20%(去掉 JSON 写不了的注释行后差距约 25%)。第二,开头那行注释 # Web 服务器配置只有 YAML 能保留,转成 JSON 后这条信息就没了。

数据类型:YAML 有而 JSON 没有的

两种格式都支持字符串、数字、布尔值、null、对象、数组。YAML 额外支持几种类型,写配置文件时特别方便。

日期和时间戳

YAML 解析器把 ISO 8601 格式的日期当作一等公民来识别。

release_date: 2026-04-01
created_at: 2026-04-01T09:30:00Z

JSON 没有日期类型,只能当字符串存。接收端得自己解析。

{
  "release_date": "2026-04-01",
  "created_at": "2026-04-01T09:30:00Z"
}

多行字符串

YAML 有两种写法处理长文本。字面块(|)保留换行,折叠标量(>)把换行替换成空格。

description: |
  这个服务接收 GitHub 的 Webhook,
  然后转发到内部队列。

summary: >
  所有流量通过 Cloudflare 代理,
  源站限速 100 rps。

JSON 要在同一行里用 \n 转义:

{
  "description": "这个服务接收 GitHub 的 Webhook,\n然后转发到内部队列。\n",
  "summary": "所有流量通过 Cloudflare 代理,源站限速 100 rps。"
}

隐式类型推断

YAML 会尝试从无引号的值推断类型。根据规范版本不同,yesnoonoff 可能被解析为布尔值,看起来像数字的值会变成数字。人写的时候方便,但也是隐蔽 bug 的来源。最出名的例子就是"挪威问题"——country: NO 被解析成布尔值 false。JSON 里所有字符串都必须加引号,类型都是显式的,不会有这种歧义。

注释:YAML 在配置文件上的天然优势

YAML 注释从 # 开始,到行尾结束。用来写"为什么设这个值"、"有什么约束"非常实用。

retries: 3  # 再大就超过上游超时了

JSON 标准(RFC 8259)没有注释语法。所以出现了 "_comment": "..." 这种假字段、或者用非标准的 JSONC / JSON5 来绕过限制的做法——都是为了弥补这个缺陷。JSON 注释的替代方案详见 JSON 可以注释吗

锚点和别名:YAML 的复用机制

YAML 允许定义一次值,在多处引用。用 & 设锚点,用 * 做别名引用,<<: 合并键可以把公共块拉进来。

defaults: &defaults
  adapter: postgres
  host: db.internal
  pool: 5

development:
  <<: *defaults
  database: myapp_dev

production:
  <<: *defaults
  database: myapp_prod
  pool: 20

转成 JSON 后,锚点被内联展开,developmentproduction 各拿到一份完整的默认值副本。数据没错,但"共享同一组默认值"这个原始意图在 JSON 结构里看不出来了。

解析性能和文件大小

机器间传数据,JSON 更快。主流语言运行时都内置了高度优化的 JSON 解析器(JSON.parsejson.loadsencoding/json)。YAML 语法复杂、规则多,解析速度慢,大部分 YAML 库内部也是构建一棵 JSON 等价的树。

文件大小取决于"跟哪种 JSON 比"。用本站做令牌测试的同一份 32 行样本(scripts/benchmarks/yaml-vs-json-tokens/)实测:YAML 667 字节,带缩进的 JSON 857 字节,YAML 小约 22%。但去掉所有换行和空格的压缩 JSON 是 686 字节,跟 YAML 几乎一样。传输前走一遍 gzip 压缩的话,这点差距也基本消失。YAML 真正的成本不在字节数,而在解析时的 CPU 开销和隐式类型推断带来的意外行为。

什么时候该选 YAML

  • 文件是人读、人手动编辑的
  • 是配置文件,不是数据载荷
  • 需要注释来解释配置意图
  • 工具链已经默认 YAML(Kubernetes、Docker Compose、Ansible、CI)
  • 想用锚点和别名减少重复

手头有一份 JSON 配置,想转成 YAML 提高可读性,可以用 JSON to YAML 转换工具,嵌套结构和缩进在浏览器里直接看。更多关于 JSON 转 YAML 的方法,参见 JSON 转 YAML 指南

什么时候不该用 YAML

看起来该用 YAML 的场景,实际用反而出问题:

  • 程序自动生成的配置文件——隐式类型推断会把 version: 2.0 变成数字 2,数据悄悄被改
  • 实时高性能数据流——解析开销在高吞吐下不可忽略
  • 安全敏感的输入——YAML 的 !!python/object 标签在 unsafe loader 下可以执行任意代码。务必用 yaml.safe_load 或等效的安全加载器
  • 短而扁平的数据——缩进省不了多少,JSON 反而更直观

什么时候该选 JSON

  • 数据的生产和消费都是程序
  • 需要跨语言互操作,不想有隐式类型转换
  • 数据通过 API 在网络上传输
  • 要求解析结果严格一致、没有歧义
  • 性能敏感,需要解析快且可预测

人写的 YAML 要交给只认 JSON 的程序处理,这种情况日常很常见。用 YAML to JSON 转换工具可以在浏览器里转,语法错误会带行号提示,方便在部署前发现被类型推断改掉的值。

决策矩阵:YAML 还是 JSON,一表看清

把上面的选型标准浓缩成一张表。在左列找到你的场景,看对应行的结论。

判断标准YAMLJSON
人手动编辑合适——符号少、能写注释不合适——符号多、不能写注释
机器间数据交换不合适——解析慢、标量类型模糊合适——有快速的原生解析器
需要注释原生支持(#不支持(JSONC/JSON5 是非标准替代)
文件内复用锚点、别名、合并键没有——只能重复或改结构
类型行为隐式推断,因解析器而异(见下方实测表)所有环境严格一致
解析器可用性多数语言需要外部库所有主流运行时内置
单文件多文档支持(--- 分隔)不支持——一个文件一个值
多行字符串专用语法(|>一行内用 \n 转义
LLM 令牌成本比带缩进的 JSON 少压缩 JSON 最少
主要使用场景Kubernetes、CI、Docker Compose、配置文件API、日志、数据管道、存储

YAML 转 JSON 时的常见陷阱

转换大体上是有损的,但有几个边界情况容易翻车。

  • 注释丢失——JSON 没有注释语法,所有 # 开头的行全部被丢掉
  • 锚点展开——共享块被复制到每个引用位置,数据重复
  • 日期变字符串——ISO 日期丢失类型信息,变成普通字符串
  • 类型推断的意外结果——version: 2.0 这种 YAML 值在 JSON 里可能变成数字 2
  • 多文档处理——--- 分隔的多个 YAML 文档没法塞进一个 JSON 文件,要么选一个,要么用外层数组包起来
  • 重复键——YAML 的行为因实现而异,JSON 里只保留最后一个值
  • 标签和自定义类型——YAML 的 !!timestamp 或自定义标签在 JSON 里没有对应类型

实测:同一份 YAML,4 个解析器,4 种结果

"类型推断的意外结果"听起来抽象,直到你看到同一行 YAML 在不同库面前变成不同的值。把一组有歧义的标量值分别喂给 4 个主流解析器(js-yaml 4.1.1、yaml/eemeli 2.8.3、PyYAML 6.0.3 safe_load、ruamel.yaml 0.19.1),复现脚本和原始数据在 scripts/benchmarks/yaml-parser-differences/,2026-07-10 在 Apple M5 Pro 上测的。转出来的 JSON 里实际拿到的是:

无引号 YAML 值js-yaml 4.1.1yaml (eemeli) 2.8.3PyYAML 6.0.3ruamel.yaml 0.19.1
NO(挪威问题)"NO""NO"false"NO"
on"on""on"true"on"
010(旧式八进制)1010810
0o10(YAML 1.2 八进制)88"0o10"8
1:30(六十进制)"1:30""1:30"90"1:30"
2026-07-10(裸日期)Date 对象"2026-07-10"date 对象date 对象
a: 1 + a: 2(重复键)报错报错后者优先(2报错
<<: *base(合并键)正常合并保留字面 "<<"正常合并正常合并

从转换角度看,有三个发现很重要。第一,挪威问题不是"YAML 的锅"——4 个解析器里只有 PyYAML(YAML 1.1 系加载器)把 NO 变成 false,另外 3 个遵循 YAML 1.2 的解析器保留字符串 "NO"。第二,裸日期分成三种情况:eemeli 的 yaml 返回纯字符串,js-yaml、PyYAML、ruamel.yaml 生成日期对象,后续 JSON 序列化时你得另外处理。第三,合并键 <<: 在 eemeli yaml 默认配置下行为不同:不合并共享块,而是把字面 "<<" 键留在 JSON 输出里——这种数据损坏往往要到下游服务报错才发现。

如果你的流水线里用的解析器不可控,转之前把有歧义的值("NO""1:30""2026-07-10")加上引号最保险。拿一份有代表性的文件粘贴到 YAML to JSON 转换工具,部署前就能在浏览器里看到每个值会变成什么。YAML 转 JSON 的完整陷阱清单见 YAML 转 JSON 指南

用 FormatArc 转换 YAML 和 JSON

FormatArc 所有转换在浏览器内完成。不用上传文件,不用注册账号,数据不出你的机器。

在 FormatArc 的 YAML to JSON 工具中,左侧输入 YAML 配置,右侧显示转换后的 JSON 结果在 FormatArc 的 YAML to JSON 工具中,左侧输入 YAML 配置,右侧显示转换后的 JSON 结果

  • YAML to JSON——粘贴 YAML 得到 JSON,语法错误时显示行号
  • JSON to YAML——反向转换,输出保持嵌套结构的整洁 YAML
  • JSON 格式化——格式化转换后的 JSON,验证语法错误

脚本或自动化流水线里处理的话,yq在新标签页中打开 或 Python 的 yaml.safe_load 是常规选择。Python 一行命令:

python3 -c "import yaml,json,sys; print(json.dumps(yaml.safe_load(sys.stdin),indent=2))" < config.yaml

常见问题

YAML 是 JSON 的超集吗?

YAML 1.2 规范起,合法的 JSON 也是合法的 YAML。也就是说 YAML 解析器能直接读 JSON 文件。反过来不行——JSON 解析器读不了 YAML。不过很多 YAML 库默认还是用旧的 YAML 1.1 规范,兼容性不是 100%。

Kubernetes 和 Docker Compose 为什么用 YAML 而不是 JSON?

因为配置文件是人读人写的。基于缩进、支持注释的 YAML 在维护大型基础设施配置时更友好。需要输出状态的时候(比如 kubectl get ... -o json),这些工具都会另外提供 JSON 模式给机器用。

YAML 解析比 JSON 快吗?

不是。JSON 解析在所有主流语言里都快得多。JSON 解析器高度优化,YAML 语法丰富且有歧义,解析自然慢。性能敏感的数据传输场景,JSON 是更好的选择。

JSON 转 YAML 会丢数据吗?

通常不会。JSON 功能更简单,JSON 转 YAML 几乎无损。你没法加上 JSON 原本没有的注释,但数据本身可以完整往返。

为什么 YAML 里写 country: NO 会变成 false

这就是"挪威问题"。旧版 YAML 解析器(YAML 1.1 规范)把无引号的 NO 解析为布尔值 false。写成 country: "NO" 加引号就解决了。YAML 1.2 规范已经去掉了这个行为,但很多工具默认还是 1.1。

喂给 LLM 提示词,YAML 和 JSON 哪个更省令牌?

取决于你拿哪种 JSON 来比。本站用 32 行 Kubernetes 风格样本(scripts/benchmarks/yaml-vs-json-tokens/)配合 OpenAI 的 tiktoken 库实测。

跟带缩进的 JSON(json.dumps(data, indent=2))比,YAML 在 cl100k_baseo200k_base 两种编码下都省约 22% 令牌(YAML 218 个 token vs 带缩进 JSON 281 个)。

但跟压缩 JSON(json.dumps(data, separators=(",", ":")))比,结果反转,JSON 反而省约 18%~21%(压缩 JSON 180 个 token)。

嵌套很深的 JSON,每个 {}[]" 都单独消耗一个 token,而 YAML 用缩进表达层级、不写闭合括号。JSON 去掉空白后差距就抹平了。

实际选型的判断标准:

  1. 能接受压缩 JSON 的场景,JSON 最省
  2. 人也要读的配置、或者包含多行字符串的数据,YAML 可读性好且总体更省
  3. 部分模型对严格 JSON 输出更稳定,所以 2026 年常见的模式是"输入用 YAML、输出要求 JSON"的不对称用法

YAML 的注释转成 JSON 后还在吗?

不在了。注释是绑定在源文件行号上的信息,一旦解析成结构化数据对象就丢掉了。转成 JSON 后无法恢复。需要保留注释的话,把原始 YAML 文件留着。

总结

  • YAML 和 JSON 描述同一套数据模型,区别在于"谁在读"
  • 人手编辑的配置文件用 YAML,机器间的数据交换用 JSON
  • 转换时注意隐式类型推断、锚点展开、注释丢失
  • FormatArc 在浏览器里完成双向转换,语法错误带行号提示