发布于 · 阅读约 2 分钟
JSON vs YAML vs CSV:该用哪种数据格式?
JSON、YAML 和 CSV 解决的问题有重叠,但它们并非为同一份工作而生。本文深入对比语法、文件大小、工具生态以及那些在生产环境里咬人的小坑,让你能有底气地选对格式。
如果你写过几年软件,几乎肯定亲手把这三种格式都敲进过文件。JSON 从每一个 REST 接口里涌出来;YAML 是 Kubernetes 清单、GitHub Actions 工作流和你装的一半 CLI 的通用语;CSV 是市场部门跟你要「原始数据」时寄给你的东西。它们看上去可以互换,像 Multilities 这样的工具也能让转换变得轻而易举,但「可以转换」不等于「随便挑一种」。
本文会拆解每种格式的工作方式、各自擅长什么、在哪里会崩,以及成熟团队挑选格式时遵循的经验法则。我们会用同一份数据写出三种格式,让取舍变得具体而不是抽象。
一个快速心智模型
在被语法淹没之前,先这样在脑子里把它们摆好:
- JSON 是结构化对象的序列化格式。机器与机器对话、人偶尔看一眼输出时,应该用它。
- YAML 是 JSON 的「类超集」,为人类手动编辑配置文件而优化。它假设有人会去滚动、做 diff、做审查。
- CSV 是表格格式。它假设每行结构一致,消费方很可能是电子表格、数据库 COPY 命令,或 pandas 里的一行 read_csv。
JSON:API 与 JS 的默认选项
JSON(JavaScript Object Notation)于 2000 年代初从 JavaScript 中抽离出来,由 RFC 8259 标准化。它只有六种原始类型:对象、数组、字符串、数字、布尔和 null。这就是全部词汇。没有日期、没有注释、没有尾逗号、没有引用。这种极简是有意的:任何语言都能在几百行代码里解析 JSON——这就是为什么地球上每个 HTTP API 最终都会落到 JSON。
浏览器用 JSON.parse 原生解析 JSON,所有后端标准库都自带 JSON 模块,需要时还有 MessagePack、CBOR 等对二进制友好的变体。对请求/响应载荷、日志投递、持久化 JS 状态、文档数据库等场景,JSON 几乎都是正确的默认。
YAML:配置的甜区
YAML 最初叫 Yet Another Markup Language,后来被递归地改名为 YAML Ain't Markup Language。1.2 版本中,YAML 是 JSON 的严格超集:任何合法 JSON 都是合法 YAML,但 YAML 还增加了注释、多行字符串、用于复用的锚点与别名、显式类型标签,以及最重要的——基于缩进的语法,几乎把所有标点噪声都去掉了。
这种可读性使 YAML 占据了配置的主流。Kubernetes 清单、Helm chart、Ansible playbook、GitHub Actions、GitLab CI、Docker Compose、OpenAPI 规范、dbt 项目和绝大多数现代 CLI 都吃 YAML。当一个文件会被人在编辑器里编辑、并在 PR 中审查时,YAML 胜出。
CSV:电子表格的通用握手
CSV(逗号分隔值)早于万维网,RFC 4180 对它做过较松的描述。它简单到极致:第一行通常是表头,其后每行是一条记录,字段以分隔符分开(最常见是逗号,但 tab、分号、竖线也很常见)。包含分隔符、换行或双引号的字符串用双引号包起来,内部双引号通过翻倍来转义。
CSV 是地球上每个电子表格、每个 BI 工具、每个数据库批量导入器、每个用 Python notebook 的分析师都理所当然地能读的格式。它对嵌套数据来说糟透了,对扁平表格来说妙不可言。当消费方是开着 Excel 的人,答案几乎永远是 CSV。
同一份数据,三种格式
用一个具体例子把差异钉牢。设想一份三个用户的列表,每位用户有 id、name、email、role 和标签数组。先看 JSON 版本。
[
{
"id": 1,
"name": "Ada Lovelace",
"email": "ada@example.com",
"role": "admin",
"tags": ["founder", "math"]
},
{
"id": 2,
"name": "Linus Torvalds",
"email": "linus@example.com",
"role": "maintainer",
"tags": ["kernel", "git"]
},
{
"id": 3,
"name": "Grace Hopper",
"email": "grace@example.com",
"role": "admin",
"tags": ["compiler", "navy"]
}
]同样的载荷写成 YAML
看,标点都不见了。结构完全靠缩进表达;标签列表既可以写成内联(流式风格,与 JSON 完全一致),也可以写成块状。注释合法且经常有用。
# 预置到 staging 环境的用户
- id: 1
name: Ada Lovelace
email: ada@example.com
role: admin
tags:
- founder
- math
- id: 2
name: Linus Torvalds
email: linus@example.com
role: maintainer
tags: [kernel, git] # 短列表写流式风格也可以
- id: 3
name: Grace Hopper
email: grace@example.com
role: admin
tags:
- compiler
- navy再写成 CSV
CSV 没法原生表达嵌套的 tags 数组,所以要扁平化。常见做法是用一个次级分隔符(如竖线或分号)拼接列表,并在某处记录这个选择。表头行充当字段名,每条记录独占一行。
id,name,email,role,tags
1,Ada Lovelace,ada@example.com,admin,founder|math
2,Linus Torvalds,linus@example.com,maintainer,kernel|git
3,Grace Hopper,grace@example.com,admin,compiler|navy三种格式,三种体积
文件大小比人们承认的更重要。日志管线、浏览器打包、存储账单都关心这件事。上面这个例子的大致字节数:JSON 美化后约 410 字节、压缩后约 290;YAML 约 320 字节;CSV 约 200 字节。在扁平数据上 CSV 大获全胜,因为字段名只出现一次,而不是在每行里重复。
一旦开始嵌套,画面就反转。每条记录在 JSON 和 YAML 里大致恒定,CSV 要么用你自创的约定去扁平化,要么爆成多张表。对一条字段嵌套很深的二十字段用户记录,JSON 和 YAML 会比你能构造的任何 CSV 更小、更清楚。
现实中每种格式的胜场
对照真实团队的使用模式:
- JSON 用于 HTTP API、WebSocket 载荷、浏览器 localStorage、日志行(尤其是 JSON Lines)、MongoDB 这类文档数据库,以及任何服务间契约。
- YAML 用于 Kubernetes、Helm、Argo、Terraform Cloud workspace、GitHub/GitLab CI 流水线、OpenAPI 与 AsyncAPI 规范、需要人工编辑的应用配置,以及可读性优先于解析速度的 IaC。
- CSV 用于分析导出、CRM 导入、ETL 暂存表、机器学习数据集、财务报表,以及任何要进 Excel 或 Google Sheets 的东西。
- 如果数据是二进制、吞吐巨大、跨服务强类型,三个都不要选——Protobuf、Avro 或 Parquet 通常更合适。
JSON 的坑
JSON 的极简既是特性,也制造了一些陷阱。规范禁止注释,因此用 JSON 写的配置文件没法自我解释。最后一个数组或对象元素后的尾逗号是非法的,即便每一种现代语言都接受它。数字默认是 64 位浮点,因此大于 2 的 53 次方的整数会悄悄丢精度——这就是为什么很多 API 把大 ID 序列化为字符串。
JSON 也没有原生日期类型。ISO 8601 字符串是事实约定,但你和消费方必须先达成共识。同一对象内的重复 key 在语法上技术允许,但在不同解析器里行为未定义,不要依赖。
YAML 的坑更咬人
YAML 的灵活是它的弱点。缩进有语义,所以混用 tab 和空格、或者前导空白少两个字符,都会悄悄改变文件含义。臭名昭著的「Norway 问题」在 YAML 1.1 解析器里仍会浮现:未加引号的 NO 会被解析为布尔 false,多次毁掉国家代码列表。YAML 1.2 修复了它,但大量工具链仍带着 1.1 语义出货。
看起来像数字、日期或布尔的字符串如果不加引号就会被强转。锚点和别名(&foo 与 *foo)很强大,但接受不受信 YAML 时会引来「十亿大笑」类的 DoS。永远用安全加载器解析用户提供的 YAML。
CSV 的坑悄无声息
CSV 是最容易表面正确、实则微妙出错的格式。分隔符没有标准——欧洲地区导出常用分号,因为逗号是小数分隔符,你的导入器必须探测。引号包裹字段内允许换行,但天真的按行分割器会被它绊倒。Excel 一打开 CSV 就乐于把电话号码和长 ID 重格式化为科学计数,在人看见之前已经把数据毁了。
编码是另一颗地雷。CSV 没有编码声明,在 macOS 上能正常打开的文件,在 Windows 上可能因为 UTF-8 与 Windows-1252 的差异而出现乱码。拿不准时,写一个 UTF-8 BOM、注明分隔符、对所有字符串字段加引号。
在它们之间安全转换
多数真实系统会同时活在不止一种格式里:你从供应商那里拿到 CSV,把它转成 JSON 给 API,再生成 YAML 来配置处理结果的 worker。手工转换是 bug 的温床,这也是 Multilities 提供专用转换器的原因。
在 JSON 和 YAML 之间互转,/tools/yaml-json 双向都行,并尽量保留注释。对表格数据,/tools/csv-json 把 CSV 导出转成可直接粘进 fixture 或 Postman 请求的 JSON 数组,回头要给财务发表也能反向转回去。仅仅想让 JSON 看起来像人类写的,/tools/json-formatter 一遍内就能排序 key、修缩进、并校验结构。
决策清单
拿不准时,按下面这张表从上到下走,遇到第一个「是」就停。
- 消费方是电子表格或 SQL 批量导入器,且数据扁平?用 CSV。
- 会有人在代码编辑器里编辑、在 PR 中审查这份文件?用 YAML。
- 消费方是程序(尤其跨网络),且你想要最少仪式、最丰富的工具支持?用 JSON。
- 在固定 schema 下高吞吐量序列化数百万条记录?跳过这三种,去用 Protobuf、Avro 或 Parquet。
收尾
这三种格式与其说是竞争者,不如说是专才。JSON 是通用交换格式,YAML 是为人类友好的配置语言,CSV 是因「非开发者也普遍能看懂」而至今不死的电子表格握手。知道每种格式是为哪份工作设计的、知道它们的坑,正是「能交付干净数据管线的团队」和「每周五下午都在跟转义 bug 搏斗的团队」之间的区别。
把转换工具收藏好,记下你 CSV 用的分隔符与编码,YAML 字符串拿不准就加引号,永远不要相信一个 64 位无符号 ID 能穿过 JavaScript 来回往返。做到这些,你选的格式就会基本消失在背景里——这恰恰是好数据格式应该做到的事。