JSON 与 YAML 互转
JSON 转 YAML、YAML 转 JSON,手写解析覆盖常见结构
JSON 与 YAML 互转:类型陷阱、注释丢失与安全问题
两者的关系不是对等的
YAML 1.2 是 JSON 的超集——任何合法的 JSON 文档都是合法的 YAML(少数边缘情况除外,主要是缩进里不允许制表符)。反过来不成立。
YAML 多出来的能力正是它被广泛用于配置文件的原因:
| 能力 | JSON | YAML |
|---|---|---|
| 注释 | 不支持 | # 注释 |
| 多行文本 | 必须写成 \n 转义 | | 与 > 块标量 |
| 锚点与引用 | 不支持,只能重复写 | &anchor / *anchor |
| 标点噪声 | 引号、逗号、花括号都要写 | 缩进表达结构 |
| 可读性 | 长文档较难读 | 层次直观 |
所以转换方向的性质不同:JSON → YAML 基本无损(只是换个写法);YAML → JSON 必然丢东西——注释和锚点结构都留不下来。
YAML → JSON 会丢什么
- 注释全部消失。JSON 没有注释语法,这是硬限制。所以不要把 YAML 当唯一配置源:如果你的流程是"编辑 YAML → 转成 JSON 部署",那转出来的 JSON 不能作为下次编辑的输入,否则注释会被反复抹掉。正确的做法是保留 YAML 作为源,JSON 只作为生成产物。
- 锚点被展开。
&base定义的片段会被复制到每个引用处,结构信息丢失,文档体积可能膨胀。反向生成锚点是启发式的(判断哪几段内容完全相同),不能指望工具替你决定复用策略。 - 非字符串键会被强制转换。JSON 规定对象的键必须是字符串,而 YAML 允许键是数字、布尔甚至复杂结构。转换时这些键必须被字符串化,于是
123: value变成"123": value——原本的类型信息消失,再转回 YAML 不会自动还原。 - 多文档(
---分隔)无法用单个 JSON 表达,只能取其中一个文档,或者包成一个数组。
YAML 的类型推断是坑最多的部分
JSON 的类型来自引号和字面量形式,明确无歧义。YAML 允许不加引号,代价是解析器需要猜测类型,于是出现了一批著名的陷阱:
| 你写的值 | 解析结果 | 问题 |
|---|---|---|
NO / no / off | false | "挪威问题":国家代码 NO 变成布尔假 |
1.10 | 1.1 | 版本号被当成浮点数,尾零丢失 |
01234 | 不同解析器结果不同 | 有的按八进制、有的按十进制,邮政编码灾难 |
12:30:00 | 45000 | YAML 1.1 支持六十进制,时间被算成秒数 |
2026-01-15 | 日期对象 | 转成 JSON 时被序列化成 ISO 时间字符串,可能带时区偏移 |
yes / on | YAML 1.1 为 true,1.2 为字符串 | 同一份文档在不同版本下结果不同 |
结论很简单:凡是"看起来像数字、布尔、日期"的字符串,一律加引号。版本号 "1.10"、国家代码 "NO"、带前导零的编号 "01234",都应当写成带引号的形式。这是唯一可靠的防御方式,不能靠记忆各版本的差异。
YAML 的安全问题
- 反序列化可以执行代码。某些语言的 YAML 库支持自定义标签,加载时会实例化任意对象(Ruby 的
Psych.load、Python 的yaml.load旧版行为)。绝不要用允许任意标签的加载方式处理来自不可信来源的 YAML,要用安全模式(Python 的safe_load、带显式 schema 的加载器)。 - 锚点炸弹(billion laughs)。用嵌套的锚点引用可以构造出体积指数膨胀的文档。一个几 KB 的输入就能吃掉几 GB 内存。这也是限制锚点展开次数的原因。
- 解析器的实现差异。YAML 规范庞大而细节多,不同语言的实现(PyYAML、libyaml、go-yaml、ruamel)在边缘情况下行为不一致。跨语言交换数据应用 JSON,YAML 只适合人写、单语言读的配置。
本工具的边界
本工具的 YAML 解析是为常见结构手写的,覆盖缩进映射、序列、标量、引号字符串与注释。它不实现锚点与别名、自定义标签、多文档分隔、复杂的流式({} / [])嵌套等完整规范特性。
所以:日常编辑 Kubernetes 清单、CI 配置、docker-compose 这类文件,用它做互转和快速查看足够;要处理生产环境里由程序生成的 YAML(可能含锚点、复杂标量),请用对应语言的官方解析器,或者用命令行工具做权威转换,再拿结果回来核对。工具在遇到无法解析的结构时会报错并指出行号,不会给出一份静默出错的输出。
所有解析都在浏览器本地完成,配置文件内容不会被上传——这一点在配置文件常含内网地址、密钥名、账号信息时尤其重要。
常见问题
为什么字符串 "100" 转 YAML 后带引号?
为了避免再转回 JSON 时变成数字。YAML 默认会把纯数字识别成整数,加引号能精确保留原类型。
支持的 YAML 特性有限吗?
覆盖日常最常见的子集:映射、序列、嵌套、标量、注释。锚点(anchor)、多文档、复杂流式写法暂不支持。