YAML JSON 转换完全指南:原理、工具与实战避坑
什么是 YAML 和 JSON
在现代软件开发中,数据序列化格式无处不在。YAML 和 JSON 是两种最常用的结构化数据表示格式,它们各有特点,适用于不同的场景。
YAML 的定义和特点
YAML(YAML Ain't Markup Language)是一种人类友好的数据序列化标准,由 Clark Evans 于 2001 年首次提出。它的设计目标是让数据既易于人类阅读和编写,又便于机器解析和生成。
YAML 的核心特点:
- 可读性强:使用缩进表示层级结构,类似 Python 的语法风格
- 简洁语法:不需要引号、大括号等冗余符号
- 丰富的数据类型:支持字符串、数字、布尔值、日期、空值、列表、字典等
- 高级特性:支持锚点(&)、别名(*)、多行字符串、复杂键等
- 注释支持:可以使用
#添加注释,方便文档化
一个简单的 YAML 示例:
name: ToolVault
description: 开发者在线工具集
version: 2.0
features:
- 格式转换
- 编码解码
- 文本处理
- 加密哈希
is_free: true
founded: 2024-01-15
JSON 的定义和特点
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,由 Douglas Crockford 在 2001 年左右推广。把 API 返回的 JSON 转成 YAML 配置时,JSON 转 YAML 工具很常用。它源于 JavaScript 语言,但现在已经成为几乎所有编程语言都支持的通用格式。
JSON 的核心特点:
- 语法简洁:使用大括号
{}和方括号[]表示结构 - 机器友好:解析速度快,几乎所有语言都有原生支持
- 数据类型有限:支持对象、数组、字符串、数字、布尔值、null
- 无注释:标准 JSON 不支持注释
- 严格格式:键名必须用双引号,字符串必须用双引号
同样内容的 JSON 示例:
{
"name": "ToolVault",
"description": "开发者在线工具集",
"version": 2.0,
"features": ["格式转换", "编码解码", "文本处理", "加密哈希"],
"is_free": true,
"founded": "2024-01-15"
}
两者的关系和区别
YAML 和 JSON 有着密切的关系:JSON 在语法上是 YAML 1.2 的子集,也就是说,几乎所有合法的 JSON 都是合法的 YAML。这就是为什么 YAML 解析器通常可以直接解析 JSON 数据。
但有一个常被忽略的前提:这句话只在 YAML 1.2 的 JSON schema 下严格成立。像 {"flag": on} 这样的 JSON 输入,在 YAML 1.1 式解析器里 on 会被当成布尔值 true——语法是子集,标量解释不一定。后文「YAML 1.1 vs 1.2」一节会详细展开。
但反过来,YAML 的许多特性在 JSON 中并不存在,比如注释、锚点、多行字符串等。
为什么需要 YAML JSON 转换
在实际开发中,YAML 和 JSON 之间的转换是非常常见的需求。以下是几个典型的应用场景。
配置文件场景
YAML 因其良好的可读性,被广泛用作配置文件格式:
- Kubernetes:所有资源定义(Deployment、Service、ConfigMap 等)都使用 YAML
- Docker Compose:容器编排配置文件
docker-compose.yml - Ansible:自动化运维的 Playbook 使用 YAML 编写
- GitHub Actions:CI/CD 工作流配置
- OpenAPI/Swagger:API 文档规范通常用 YAML 编写
而这些配置工具的内部实现,往往是先把 YAML 解析成 JSON 结构再进行处理。
API 数据交换
在 Web API 领域,JSON 是事实上的标准数据交换格式。当你需要:
- 把 YAML 配置的内容通过 API 发送给后端
- 将 API 返回的 JSON 数据保存为 YAML 配置文件
- 在支持 YAML 的工具和支持 JSON 的系统之间传递数据
这些场景都需要进行格式转换。
前后端协作
前端开发者通常更熟悉 JSON,而后端或运维工程师可能更习惯用 YAML 编写配置。在团队协作中,经常需要:
- 后端提供 JSON 格式的 API 响应,前端需要转为 YAML 做配置
- 运维用 YAML 写配置,开发需要转为 JSON 在代码中使用
- 技术文档中同时提供两种格式的示例
YAML vs JSON 对比
让我们通过一个详细的对比表,来全面了解这两种格式的差异。
语法对比表格
| 特性 | YAML | JSON |
|---|---|---|
| 语法风格 | 缩进表示层级 | 大括号和方括号 |
| 注释 | 支持(#) | 不支持 |
| 字符串引号 | 可选,大多数情况不需要 | 必须使用双引号 |
| 键名引号 | 可选 | 必须使用双引号 |
| 多行字符串 | 原生支持(` | 、>`) |
| 数据类型 | 丰富(日期、时间、空值等) | 有限(6 种基本类型) |
| 锚点/别名 | 支持(&、*) | 不支持 |
| 文件扩展名 | .yaml / .yml | .json |
| 人类可读性 | 高 | 中等 |
| 解析速度 | 相对较慢 | 快 |
| 语言支持 | 需要第三方库 | 大多数语言原生支持 |
各自的优势和适用场景
YAML 的优势:
- 更适合人类阅读和编写
- 支持注释,便于文档化
- 多行字符串处理更自然
- 可以用锚点和别名减少重复
- 适合作为配置文件格式
JSON 的优势:
- 解析速度更快
- 几乎所有编程语言原生支持
- 语法严格,不容易产生歧义
- 适合作为 API 数据交换格式
- 工具生态更完善
一句话总结:配置文件用 YAML,数据交换用 JSON。
YAML 转 JSON 的方法
下面介绍几种常见的 YAML 转 JSON 方法,从最简单的在线工具到命令行和代码实现。
在线工具转换
最快捷的方式是使用在线转换工具,无需安装任何软件。
使用 ToolVault 工具匣 YAML JSON 转换工具:
- 在左侧输入框粘贴 YAML 内容
- 右侧自动实时显示转换后的 JSON
- 点击复制按钮即可获取结果
这种方式适合临时转换、快速验证,以及不想安装工具的场景。
命令行工具
对于开发者来说,命令行工具效率更高,特别适合批量处理。
使用 yq
yq 是一个专门处理 YAML 的命令行工具,类似于处理 JSON 的 jq。
# 将 YAML 文件转为 JSON
yq eval -o=json input.yaml > output.json
# 或者使用简写
yq -o=json input.yaml
使用 Python
Python 标准库自带 JSON 模块,YAML 则需要安装 PyYAML:
# 安装 PyYAML
pip install pyyaml
# 单行命令转换
python -c "import yaml, json, sys; print(json.dumps(yaml.safe_load(sys.stdin), indent=2, ensure_ascii=False))" < input.yaml > output.json
编程语言实现
在项目代码中进行转换,是最灵活的方式。
JavaScript / Node.js
使用 js-yaml 库:
npm install js-yaml
const fs = require('fs');
const yaml = require('js-yaml');
const yamlContent = fs.readFileSync('input.yaml', 'utf8');
const data = yaml.load(yamlContent);
const jsonContent = JSON.stringify(data, null, 2);
console.log(jsonContent);
Python
import yaml
import json
with open('input.yaml', 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
with open('output.json', 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
提示:使用
yaml.safe_load()而不是yaml.load(),可以避免 YAML 反序列化安全问题。
JSON 转 YAML 的方法
JSON 转 YAML同样有多种实现方式。
在线工具
使用在线工具是最快的方式之一。
ToolVault 工具匣 YAML JSON 转换工具 支持双向转换:
- 点击中间的"切换方向"按钮,切换到 JSON → YAML 模式
- 在左侧输入 JSON 内容
- 右侧实时显示转换后的 YAML
命令行
使用 yq
# 将 JSON 文件转为 YAML
yq eval -P input.json > output.yaml
使用 Python
python -c "import yaml, json, sys; print(yaml.dump(json.load(sys.stdin), allow_unicode=True, default_flow_style=False))" < input.json > output.yaml
代码实现
JavaScript / Node.js
const fs = require('fs');
const yaml = require('js-yaml');
const jsonContent = fs.readFileSync('input.json', 'utf8');
const data = JSON.parse(jsonContent);
const yamlContent = yaml.dump(data, { indent: 2, lineWidth: -1 });
console.log(yamlContent);
Python
import json
import yaml
with open('input.json', 'r', encoding='utf-8') as f:
data = json.load(f)
with open('output.yaml', 'w', encoding='utf-8') as f:
yaml.dump(data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
常见问题和陷阱
YAML 和 JSON 之间的转换看起来简单,但实际操作中经常会遇到各种问题。下面是一些最常见的陷阱。
缩进问题
YAML 对缩进非常敏感,这是新手最容易踩的坑:
- 必须使用空格缩进,不能用 Tab
- 同一层级的缩进必须对齐
- 通常使用 2 个或 4 个空格,但一个文件内要一致
# 正确
person:
name: Alice
age: 30
# 错误:缩进不一致
person:
name: Alice
age: 30 # 多了一个空格
解决方法:使用支持 YAML 的编辑器,开启显示空白字符功能;或者使用 ToolVault 工具匣 的 YAML 格式化工具 自动修复格式。
数据类型丢失
YAML 的自动类型推断有时会带来意外。但要先明确一个关键事实:这类坑绝大多数属于 YAML 1.1 的解析行为。YAML 1.2(2009 年发布)把布尔值收紧为只有 true / false,整数十进制化,yes/no/on/off、前导零八进制、六十进制全部回归普通字符串。新一代解析器(go-yaml v3、js-yaml、ruamel.yaml、yamllint)都按 1.2 规则解析;仍按 1.1 解析的著名例外是 PyYAML。
用 js-yaml 5.x(本工具所用库,默认 DEFAULT_SCHEMA,按 1.2 core schema 解析)实测:
# YAML 1.1 解析器(如 PyYAML)的行为 # js-yaml / YAML 1.2 的行为
enabled: yes # → true(布尔) # → "yes"(字符串)
flag: off # → false # → "off"
port: 08080 # → 520(前导零按八进制!) # → 8080(十进制)
base: 0o17 # → 15 # → 15(0o 前缀八进制仍然有效)
date: 2024-01-15 # → Date 对象 # → 字符串 "2024-01-15"
注意 js-yaml 下 2024-01-15 解析出来就是字符串(核心 schema 没有日期类型),不是 Date 对象——网上一些教程说 YAML 会把日期转成 Date,那是其他库(或旧版本)的行为。
挪威问题(The Norway Problem)
YAML 1.1 的布尔词表臭名昭著:y / n / yes / no / true / false / on / off(含各种大小写)共 22 种写法全是布尔值。于是:
countries:
- NO # 挪威国家代码,YAML 1.1 下被解析成 false!
- CN
把国家列表从 YAML 1.1 系统迁到 YAML 1.2 系统(或反过来),NO 会在「布尔 false」和「字符串 "NO"」之间漂移,这就是配置社区常说的「挪威问题」。同样中招的还有 1:30 这类值——YAML 1.1 会把它按六十进制(sexagesimal)解析成 90,Docker Compose 的 ports 不写引号 8080:80 就曾因此踩坑(详见 docker run 转 compose 指南 中对端口引号的讨论)。
解决方法:
- 需要保留为字符串的值一律加引号:
port: "08080"、enabled: "yes"、country: "NO" - 升级工具链到按 YAML 1.2 解析的版本(go-yaml v3+、js-yaml、ruamel.yaml),PyYAML 项目注意它至今仍是 1.1 行为
- 转换后用 JSON Schema 之类做类型校验,而不是肉眼抽查
循环引用
JSON 不支持循环引用,但 JavaScript 等语言中的对象可能存在循环引用。来源有两个:业务代码里手动构造的环,以及 YAML 锚点/别名在解析后天然形成的共享结构(见上一节对锚点的讨论):
const obj = { name: 'test' };
obj.self = obj; // 循环引用
JSON.stringify(obj); // 报错:TypeError: Converting circular structure to JSON
解决方法:
- 避免在需要序列化的数据中使用循环引用
- 使用专门的库处理循环引用(如
circular-json,但已废弃)
多行字符串处理
YAML 的多行字符串有多种语法,转换时需要注意:
# | 保留换行符
description: |
第一行
第二行
# > 折叠换行(变成空格)
summary: >
这是一段
很长的文字
转为 JSON 后分别是:
{
"description": "第一行\n第二行\n",
"summary": "这是一段 很长的文字\n"
}
注意:| 和 > 语法末尾的换行符处理方式不同,需要根据实际需求选择。
YAML 锚点和别名无法转 JSON
YAML 的锚点(&)和别名(*)是非常有用的特性,可以减少重复:
defaults: &defaults
adapter: postgres
host: localhost
development:
<<: *defaults
database: dev_db
production:
<<: *defaults
database: prod_db
上面 <<: *defaults 的合并键语法让 development 同时得到 adapter、host、database 三个字段。但 JSON 没有锚点和合并键的概念,转换后会变成展开的完整结构。虽然数据是正确的,但会失去「引用」的语义。
合并键 << 的边界更要小心。 上面的 <<: *defaults 合并键不在 YAML 1.2 核心规范里,只是类型仓库中的一个可选标签,各解析器支持程度不一:
- PyYAML、go-yaml v3 支持合并键,
development会正确得到adapter/host/database三个字段 - js-yaml 也支持
<<合并,但只有当合并键直接写在映射下时才生效;如果把它写进花括号内联映射(如x: {<<: *d, b: 2}),它会被当成一个名叫<<的普通键 - 跨文档锚点是禁止的:别名只能引用同一文档内已定义的锚点,多文档流里
doc2引用doc1的锚点会直接报错
锚点还有三个容易踩的边界:
- 别名是共享引用,不是拷贝。 在 JavaScript 里,
*defaults解出来的对象和&defaults指向同一内存。如果别名构成环(a: &x { self: *x }),转 JSON 时JSON.stringify会直接抛Converting circular structure to JSON——这正是下一节的问题来源之一。 - 锚点可以覆盖。 同一文档内后出现的
&defaults会覆盖先出现的,别名解析到「它之前最近的一个」。 - 别名不能被覆盖为映射做 merge 以外的用途。 想给某个环境只改一个字段,用合并键而不是在别名后追加键。
解决方法:如果需要保留引用关系,需要在业务代码中自己处理,或者选择其他支持引用的格式。转换前先用 yamllint 或解析器把锚点全部展开确认结果符合预期。
多文档流(--- / ...)
Kubernetes 清单、Helm 模板、CI 配置里经常一个文件装多个 YAML 文档,用 --- 分隔、... 结束:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
这是 YAML 独有、JSON 完全没有对应物的结构。 各工具的处理方式差异很大:
- js-yaml 的
load()遇到多文档会直接报错(expected a single document in the stream)——这不是 bug,是刻意设计,防止静默丢数据 - 要转多文档,得用
loadAll()拿到数组再逐个JSON.stringify yq默认对每个文档输出一条 JSON;jq -s可以收拢成数组- Kubernetes 工具链(kubectl apply -f)原生接受多文档流
实用建议:把多文档 YAML 转 JSON 时,先确认下游期望的是「JSON 数组」还是「逐行 JSON(JSON Lines)」,两种形态都有人用。单文档转换器遇到 --- 报错时,先检查输入是不是多文档。
常见 lint / 解析错误排查
YAML 报错信息通常带行号列号,但有几类错误的原因不直观:
| 报错(js-yaml 原文) | 真实原因 | 排查动作 |
|---|---|---|
bad indentation of a mapping entry | 同级键缩进不一致,或混用了 Tab | 编辑器打开「显示空白字符」,全局把 Tab 换成空格 |
duplicated mapping key | 同一映射里键名重复(js-yaml、yamllint 默认报错;PyYAML 默认静默后者覆盖前者) | 全局搜重复的键名;多人协作的 CI 配置最常见 |
unexpected end of the stream within a flow collection | [ 或 { 没闭合 | 从报错行往回找未闭合的括号 |
can not read an implicit mapping pair; a colon is missed | 值里带了 : (冒号+空格)却没加引号,如 time: 10:30 | 给含冒号的值加引号:time: "10:30" |
missed comma between flow collection entries | 行内写 key: value 时 value 含特殊字符 | 同上,加引号;或改成块式写法 |
通用排查顺序:先 yamllint -d relaxed file.yaml 跑一遍;报错行看不懂时,把文件缩短到只剩报错行附近的最小片段再解析;还不行就把可疑值全部加引号试一遍——YAML 里 90% 的诡异解析错误都能用「加引号」解释。
使用 ToolVault 工具匣 在线 YAML JSON 转换工具
如果你需要快速、方便地进行 YAML 和 JSON 之间的转换,ToolVault 工具匣 的在线 YAML JSON 转换工具 是一个绝佳选择。
双向转换
工具支持 YAML → JSON 和 JSON → YAML 双向转换,一键切换方向,满足各种转换需求。无论是把配置文件转为 API 可用的 JSON,还是把接口返回的数据保存为 YAML 配置,都能轻松搞定。
实时预览
输入内容的同时,右侧结果实时更新,无需点击转换按钮。你可以边写边看转换结果,快速验证格式是否正确。
格式校验
工具会自动检测语法错误,并给出清晰的错误提示,帮你快速定位问题。无论是 YAML 的缩进问题,还是 JSON 的语法错误,都能一目了然。
此外,工具还支持:
- 格式化美化:自动调整缩进和换行,让代码更易读
- 压缩/最小化:JSON 可以转为压缩格式,减少体积
- 大整数保护:JSON → YAML 方向对超出安全整数范围的数字自动字符串化,避免雪花 ID 之类被静默改写(详见文末实现注记)
- 一键复制:转换结果一键复制到剪贴板
- 纯前端运行:所有处理在浏览器本地完成,数据不上传服务器,保护隐私
最佳实践
1. 选择合适的格式
- 配置文件:优先用 YAML,可读性好,支持注释
- API 数据交换:用 JSON,兼容性好,解析速度快
- 存储大量数据:考虑更紧凑的格式(如 MessagePack、Protocol Buffers)
2. 保持风格一致
在同一个项目中,保持格式风格的一致性:
- YAML 缩进统一使用 2 空格或 4 空格
- JSON 的缩进空格数保持一致
- 命名风格统一(驼峰、下划线、连字符选一种)
3. 注意数据类型
- 字符串如果可能被误解为数字、布尔值或日期,务必加引号
- 浮点数精度问题要注意,尤其是金额相关的数据
- 空值统一使用
null(JSON)或null/~(YAML)
4. 转换后验证
转换完成后,不要直接使用,先做验证:
- 检查关键字段的值是否正确
- 验证数据类型有没有发生意外变化
- 确认没有丢失字段或数据
5. 安全地解析 YAML
- 永远使用
safe_load而不是load,避免反序列化攻击 - 不要解析来自不可信来源的 YAML 文件
- 对解析后的数据做合法性校验
FAQ
YAML 和 JSON 哪个更好?
没有绝对的"更好",取决于使用场景。配置文件推荐用 YAML(可读性好、支持注释),API 数据交换推荐用 JSON(兼容性好、解析快)。
为什么 YAML 文件有时候会解析出错?
最常见的原因是缩进问题。YAML 对缩进非常敏感,必须使用空格(不能用 Tab),同一层级的缩进必须严格对齐。另外,特殊字符、未加引号的字符串也可能导致解析错误。
JSON 支持注释吗?
标准 JSON 不支持注释。但有些工具和库(如 JSON5、JSONC)扩展了 JSON 语法,支持注释。如果你需要带注释的配置文件,建议直接使用 YAML。
YAML 转 JSON 后数据类型变了怎么办?
先确认方向:是解析时就变了(YAML 1.1 式解析器把 yes 变 true、08080 变 520),还是序列化时变了(日期对象被 toJSON 成字符串、大整数超出 2^53 被截断)。前者靠加引号或升级解析器解决;后者要在转换前对数据做预处理。对字符串可能被误解的场景,统一加引号是成本最低的防御。
为什么我的多文档 YAML 转 JSON 报错了?
单文档转换器(如 js-yaml 的 load())遇到 --- 分隔的多文档流会抛 expected a single document in the stream。改用支持多文档的 API(loadAll())或 yq 逐文档输出;如果转换器明确只转第一份文档,务必确认那不是静默丢数据。
有没有批量转换的方法?
有。可以使用命令行工具(如 yq)配合 shell 脚本批量处理文件,或者用 Python/Node.js 写脚本遍历目录进行转换。对于简单的临时转换,也可以使用在线工具逐个处理。
实现注记(ToolVault 转换器实际行为)
这一节描述本站转换器的真实实现(js-yaml 5.2.1,yamlToJson / jsonToYaml 两个纯函数),与其他在线工具或本地库的行为可能有出入,以你手头工具的实测为准:
- YAML → JSON:走
yaml.load()(1.2 core schema)→JSON.stringify(obj, null, 2)。锚点/别名、合并键在解析阶段就展开成普通对象;遇到多文档流会抛出expected a single document in the stream错误而不是只转第一份;重复键直接报duplicated mapping key。 - JSON → YAML:先经自研的无损解析(
parseLossless,基于 meriyah AST)把数字 token 原样保留,再把超出Number.MAX_SAFE_INTEGER的整数降级为字符串写进 YAML(js-yaml 的dump没有无损数字类型)。所以{"id": 2099774875233849345}转出的 YAML 是id: '2099774875233849345'——数字 19 位雪花 ID 不会丢精度,但类型从 number 变成了 string,下游要注意。 - 反向不对称:上述字符串化的大整数再走 YAML → JSON 时,回来的是字符串而不是原数字;而在安全整数范围内(±2^53 内)的数字可以无损往返。大整数的完整讨论见 JSON 整数精度陷阱。
- 默认 dump 参数:
indent: 2、lineWidth: 120——超过 120 字符的长字符串会被折叠换行, diff 时注意这不是数据变化。 - 纯前端运行:所有转换在浏览器本地完成,不上传服务器。
延伸阅读
- JSON 整数精度陷阱:雪花 ID 如何被静默改写——理解为什么 19 位 ID 会丢精度、本工具如何用无损解析规避
- docker run 转 docker-compose 指南——生成的 compose YAML 里端口、环境变量的引号讲究,处处是本文 1.1/1.2 陷阱的实战
- Cron 表达式详解——GitHub Actions 等 CI 的 YAML 配置里
schedule字段就是 cron,配置格式与调度语义一起看
本文出自 ToolVault 工具匣。立即体验 在线 YAML JSON 转换工具,让数据格式转换变得简单高效。其他在线工具可在 首页 找到。
相关工具
相关文章
在线 CSV 转 SQL 工具:将表格数据导入数据库
CSV 转 INSERT:空单元格→NULL、MySQL 反引号或 PostgreSQL 双引号、BOM 剥离、Markdown 竖线转义,以及与 COPY/LOAD 的分工。
HTML 表格转 JSON 怎么保留行列结构?
把网页里的 HTML 表格转成 JSON,怎么保留表头、处理合并单元格、保持嵌套结构?逐步方法并附本站 HTML 表格转 JSON 工具。
Excel 转 JSON 后日期变成数字 / 中文乱码,怎么办?
把 Excel 转成 JSON 时日期变 44927 这种数字、中文乱码、空单元格丢失怎么办?讲清 Excel 日期序列号与编码坑,并附本站 Excel 转 JSON 工具。