使用方法
- 把 JSON 粘进上面的输入框。顶层应当是一个对象数组
[{...}, {...}];顶层是单个对象时会被当成只有一条记录处理。页面打开时输入框里已经放了 15 条记录,结果区同时给出了它们的 CSV。 - 选「嵌套值的扁平化策略」。默认点号路径:
contact.city变成一列contact.city,数组按下标拆成tags.0、tags.1。改成「整体序列化」则嵌套值整块变成一个 JSON 字符串塞在一格里;改成「只保留标量」则嵌套的列直接不要。 - 看统计条:示例的六个数字是字段数 10、数据行 15、输出行数 16、输出字节 891、加 BOM 894、加引号单元格 3。这六个数字随选项实时变化。
- 在源码框里按 Ctrl + Enter(macOS 用 Cmd + Enter)重新转换;单独按 Enter 只换行。在「预览表格显示前几行」输入框里按 Enter 会直接重算。
- 拿走结果:「复制 CSV」把结果区文本送进剪贴板;「下载 CSV」导出带 BOM 的 UTF-8 文件,Excel 双击打开不乱码;「下载字段清单 JSON」导出不带 BOM 的字段定义,供程序二次读取。
这个 JSON转CSV转换器 能解决什么问题
一是把接口返回的 JSON 倒进表格软件。调接口拿到的是一段 JSON 数组,想丢进 Excel 做透视、排序、筛选,手工复制粘贴会把字段对错位。本转换器先求出所有记录的字段并集再逐格填值,所以哪怕第 6 条记录多了一个 zip、第 11 条多了一个 level,列也不会错位。
二是处理字段不齐的脏数据。真实导出里「有的记录有这个字段、有的没有」是常态。本页的做法是取并集:字段顺序按首次出现的先后排定,缺这个字段的记录在该列留空。示例里 tags.0 来自第 1 条、contact.zip 来自第 6 条、level 来自第 11 条、tags.2 来自第 12 条,最后排成 10 列。
三是不再被逗号和引号带偏。手工拼 CSV 最常见的错误是字段里有一个逗号,整行就多出一列。这个 JSON转CSV转换器对每个单元格单独判断:含逗号、含双引号、含换行的值才会被双引号包起来,值里的双引号写成两个。示例里 "含,逗号"、"He said ""hi"""、"第一行⏎第二行" 三格按这条规则处理,其余 147 格保持裸值。
四是还原成真正的二维表。CSV 没有嵌套概念,tags: ["vip","new"] 这种结构必须先摊平。点号路径策略会把它变成两列 tags.0 与 tags.1,做列筛选和透视时比一整串 JSON 好用得多。
本页不做这些事:不合并单元格、不做多行表头、不做类型推断(不会把 007 当数字、也不会把 true 转成布尔)、不把 CSV 转回 JSON(反方向请用页面底部的工具)、不校验字段的业务含义。它只负责把 JSON 忠实、可逆地摊成一张符合 RFC 4180 的表。以上四件事合起来,就是 JSON转CSV转换器 的全部职责。
转换原理:递归扁平化、字段并集与 RFC 4180 转义
① 调用的函数与算法。三步,每步都是明确的名字:解析用 JavaScript 内置的 JSON.parse(实现 ECMA-404 / RFC 8259);扁平化用自写的递归函数 flatten,深度优先遍历对象与数组,把访问路径拼成字段名;序列化用自写的 esc 逐格判断是否需要加引号。没有任何第三方 CSV 库参与。
② 点号路径是怎么拼出来的。递归时带一个 path 参数:进入对象的某个键,就拼成 父.键;进入数组的第 i 项,就拼成 父.i;到了标量才把 [path, 值] 记下来。所以 {"contact":{"city":"北京"}} 产出字段 contact.city,{"tags":["vip","new"]} 产出 tags.0 与 tags.1。空对象和空数组不产出任何字段——它们没有可以落脚的标量,所以第 2 条记录的 tags: [] 在 tags.0 这一列留空。
③ 字段并集的顺序规则。把所有记录按顺序各跑一遍扁平化,每得到一个字段名就检查它是否已经出现过,没出现过就追加到数组末尾。于是字段顺序 = 首次出现的先后,而不是字母序,也不是最长记录优先。示例的 10 个字段就是这么来的:第 1 条贡献 6 个,第 6 条贡献 contact.zip,第 11 条贡献 level,第 12 条贡献 tags.1 和 tags.2。想改成字母序,先转换再用「JSON按键排序工具」处理源数据即可。
④ RFC 4180 的转义规则。这份 RFC 把 CSV 定死为:字段之间用逗号分隔,记录之间用 CRLF(\r\n,即 U+000D U+000A 两个字节)分隔,最后一条记录后的 CRLF 可选;字段可以不加引号,但如果值里含逗号、双引号或换行,就必须整个用双引号包起来;引号里的双引号要写成两个双引号。本页严格照这套做,所以 He said "hi" 输出成 "He said ""hi""",第一行的引号、第二行的双写、最后一行末尾的引号一个都不少。
⑤ 输出与输入之间少了什么。JSON 的类型信息会被抹掉——CSV 只有文本。数字 1 和字符串 "1" 输出后长得一模一样;true 变成 true 四个字母;null 变成空单元格,跟「字段缺失」和「空字符串」在 CSV 里无法区分。如果下游要区分这三者,别用 CSV,改用 JSON 或 TSV 并额外带一个类型列。
⑥ 为什么默认开表头、默认开 CRLF。RFC 4180 允许表头可选,但 Excel、Numbers、pandas(read_csv 默认 header=0)都默认把第一行当列名,不开表头反而要手工补参数。CRLF 同理:这是 RFC 规定的记录分隔符,Windows 系工具和 Excel 的原生写法,只多占 15 个字节(16 行 → 15 个换行),换来的兼容性远大于这点体积。
⑦ 编码与 BOM 分岔。Excel 在 Windows 上读 UTF-8 的 CSV 时,如果没有字节顺序标记(BOM,UTF-8 下是 EF BB BF 三个字节),会按系统本地代码页解码,中文直接变乱码。所以导出的 .csv 前面加上 BOM:示例从 891 字节变成 894 字节。但 JSON 绝不能带 BOM——按 RFC 8259,JSON 文本的第一个字符必须是值的一部分,BOM 会让 JSON.parse 直接抛 Unexpected token。所以「字段清单 JSON」这个按钮导出的文件刻意不带 BOM。
完整示例(数值仅作演示)
示例条件与页面打开时完全一致:15 条记录、点号路径、带表头、CRLF。实测结果:
- 输入 17 行 / 1632 字符 / 1818 字节;
- 输出 16 行(1 行表头 + 15 行数据)/ 705 字符 / 891 字节;
- 字段并集 10 列:
id、name、tags.0、contact.email、contact.city、note、tags.1、contact.zip、level、tags.2; - 被引号包起来的单元格 3 个,对应
含,逗号、He said "hi"、第一行⏎第二行; - 导出 CSV 加 BOM 后 894 字节,字段清单 JSON 183 字节且不带 BOM。
同一份输入换扁平化策略,字段数与体积的差别很大,这就是这个选项唯一的影响面:
| 策略 | 字段数 | 字段清单 | 输出字节 |
|---|---|---|---|
| 点号路径(默认) | 10 | id · name · tags.0 · contact.email · contact.city · note · tags.1 · contact.zip · level · tags.2 | 891 |
| 整体序列化 | 6 | id · name · tags · contact · note · level | 1408 |
| 只保留标量 | 4 | id · name · note · level | 389 |
三种策略的体积顺序值得记一下:整体序列化最大(1408 字节,比点号路径多 58%),因为它把 tags、contact 两列的键名逐条重复写了 15 遍;只保留标量最小(389 字节),代价是丢掉了 contact.email 这类信息。要拿去做透视表就选点号路径,要原样搬走数据再解析就选整体序列化。
单格转义的实际效果,逐个数一遍:
| 原始值 | 输出单元格 | 为什么要这样 |
|---|---|---|
| 用户1 | 用户1 | 不含特殊字符,不加引号 |
| 含,逗号 | "含,逗号" | 含逗号,不加引号会被当成分隔符,整行多一列 |
| He said "hi" | "He said ""hi""" | 含双引号,加引号且把内部引号双写 |
| 第一行⏎第二行 | "第一行⏎第二行" | 含换行,加引号后换行留在同一格内,不新起一条记录 |
| null | (空) | null 输出为空单元格,与「缺字段」「空串」在 CSV 里不可区分 |
| =1+1 | =1+1(勾选防护后为 "'=1+1") | 等号开头会被 Excel 当公式,防护开启时前面加一个单引号 |
嵌套扁平化策略与输入范围对照表
下面把输入条件、选项和结果放在一起,方便你预判输出。表里的数字都来自上面的 15 条示例。
| 输入 / 选项 | 能否处理 | 结果 |
|---|---|---|
| 空输入或只有空白 | 拒绝 | 报「输入为空」,结果区与预览表一起清空 |
| 超过 20 万字符 | 拒绝 | 报出实际字符数并说明上限,避免超大 JSON 把页面卡住 |
| JSON 语法错误(如多余的逗号) | 拒绝 | 报出出错的行:列,例如 第 3 行第 26 列,并复述引擎的原始提示 |
值没加引号({"name": 张三}) | 拒绝 | 定位到那个非法字符的行:列,提示 JSON 的字符串必须用双引号 |
| JSON 被截断(括号没闭合) | 拒绝 | 报「输入在第 X 行第 Y 列结束」,这是复制粘贴截断最常见的形态 |
| 顶层是数组 | 处理 | 每个元素按一条记录处理 |
| 顶层是单个对象 | 处理 | 当成只有一条记录,输出 1 行数据 |
顶层是字符串 / 数字 / true / null | 拒绝 | 报出实际的顶层类型,说明需要对象或对象数组 |
空数组 [] | 拒绝 | 报「里面没有任何记录」 |
[{},{}] 全是空对象 | 拒绝 | 报「推不出字段」,而不是给一个 0 列的空 CSV |
标量数组 [1,2,3] | 处理 | 元素归到名叫 value 的单列,输出 3 行 |
| 嵌套深度任意 | 处理 | 点号路径按层级拼名;深度只受 JavaScript 调用栈限制,1 万层以内没问题 |
| 预览行数填 0 / 501 / 小数 | 拒绝 | 报「只能填 1~500 的整数」,不四舍五入 |
结果边界、精度与失败路径
- 输入上限 20 万字符(
maxlength,按 UTF-16 码元计)。超限直接拒绝并报出实际长度,因为JSON.parse本身很快,真正的开销在生成表格 DOM。 - 数字会走 IEEE 754 双精度。
JSON.parse把所有 JSON 数字读成 JS 的 Number,安全整数范围是 ±253−1,也就是 ±9007199254740991。写9007199254740993会被读成 9007199254740992,输出到 CSV 时值已经变了。订单号、身份证号、雪花 ID 这类长整数必须在源 JSON 里就用字符串(写成"9007199254740993"),转 CSV 才不会丢位。 - 小数位会被抹平。
1.0读进来是数字1,输出成 CSV 就是1而不是1.0;JSON 本身不区分 1 和 1.0,这是格式的固有性质,不是转换器自作主张。要保住末尾的 0,同样只能让源数据用字符串。做浮点运算时还要记住0.1 + 0.2的结果是0.30000000000000004,如果 CSV 里的数字是算出来的,出现一长串 9 或 4 属于正常现象。 - 科学计数法。绝对值大于等于 1e21 的数,
String()会输出指数记法,例如1e21。CSV 里出现1e+21时 Excel 仍能识别为数字,但某些只认十进制写法的系统会当成文本——遇到这种情况请让源数据用字符串。 - CRLF 比 LF 多占字节。16 行输出有 15 个换行,用 CRLF 就是 891 字节;取消勾选改回 LF 会少 15 字节。体积差异只影响传输,不影响能否被正确解析。
- BOM 只加给 CSV。891 → 894 字节,正好三个字节
EF BB BF。给 JSON 加 BOM 会让JSON.parse抛Unexpected token,所以字段清单 JSON 这条路径永远不会带 BOM。 - 加引号的单元格是「按需」的。示例 150 个数据单元格里只有 3 个被引号包住,其余保持裸值。这是刻意的:RFC 4180 说「可以」加引号,不加才更像人手写出来的 CSV,也更省字节。反过来说,如果你想验证某个 CSV 阅读器的实现是否正确,用这一份含 3 个特殊格的样例对着读一遍最有效。
- 公式注入是数据安全边界。CSV 里以
=、+、-、@开头的单元格,被 Excel 打开时会被当公式执行,可能触发外部请求。防护的做法是在前面加一个单引号并强制加引号包住,代价是值被改写了——所以这个选项默认关闭,由你决定要不要为下游的 Excel 用户扛这一刀。 - 不推断类型、不格式化数字。工具不会把
"007"变成7,也不会给数字加千分位或补小数位;写进 CSV 的就是String(值)。下游要什么样的数字格式,请在导入后再处理。
常见问题
为什么导出 CSV 要加 BOM,字段清单 JSON 却坚决不加?
Excel 在 Windows 上读 .csv 时,如果文件没有字节顺序标记,会按系统本地代码页而不是 UTF-8 解码,中文就变乱码;加上 EF BB BF 三个字节后 Excel 才会按 UTF-8 读。JSON 正好相反,RFC 8259 规定 JSON 文本第一个字符就必须属于那个值,BOM 会让 JSON.parse 抛出 Unexpected token。所以本页两种导出走两条路:CSV 带 BOM,JSON 不带。
嵌套对象和数组,到底该选哪种扁平化策略?
看下游怎么用。要丢进 Excel 做透视、排序、按城市或邮箱筛选,选点号路径,字段变成 contact.city 这种独立列最好用。要原样把数据搬到另一个系统再解析,选整体序列化,嵌套部分保持 JSON 结构不丢,代价是体积从 891 涨到 1408 字节。只想看几个标量字段的清单,选只保留标量,输出最干净(4 列、389 字节)。
我的长订单号变成别的数字了,是工具出 bug 吗?
不是,是 JSON 数字本身的精度上限。JSON 的数字按 IEEE 754 双精度读取,能精确表示的最大整数是 253−1 = 9007199254740991。9007199254740993 会被读成 9007199254740992,最后一位变了。唯一的解法是让源 JSON 把这类字段写成字符串:"orderId": "9007199254740993",这样它走文本路径,一个字符都不会丢。
字段顺序能不能按字母排?
本页按首次出现的先后排,因为这种顺序保留了源数据里字段的自然排布,通常比字母序更贴近业务含义。想按字母排,先把源 JSON 用「JSON按键排序工具」处理一遍再转,或者在表格软件里导入后自行排序。工具不会替你重排字段名,因为那会改变输出对源数据的可追溯性。
空数组和 null 为什么都变成空白单元格?
因为 CSV 里这三件事长得完全一样:字段缺失(记录里根本没这个键)、值为 null、值为空字符串 ""。示例里第 2 条的 tags: [] 在 tags.0 列留空,第 4 条的 email: null 在 contact.email 列留空,看起来没区别。如果下游必须区分它们,就不要用 CSV,改用保留类型的格式,或者在转换前给源数据补一个标记字段。
Excel 打开导出的 CSV,中文还是乱码怎么办?
先确认用的是「下载 CSV」而不是「复制 CSV」再手动保存——只有前者会写 BOM。如果已经带 BOM 还是乱码,多半是 Excel 之外的工具重新保存过文件、把 BOM 丢了。可以改用「数据 → 从文本/CSV」导入并手动指定 UTF-8 编码,或者直接用记事本(Windows 11 的记事本默认 UTF-8)打开确认文件本身是否正确。
CSV 公式注入是什么?要不要打开防护?
如果某个单元格的值以 =、+、-、@ 开头,Excel 会把它当公式执行,恶意数据可以借机制造外部请求或篡改显示内容。防护的做法是在值前面加一个单引号并强制加引号包住,让 Excel 把它当普通文本。代价是数据被改写了,所以默认关闭——只有当这份 CSV 会被别人用 Excel 直接双击打开时才建议勾上。
结果区里为什么有一行看起来断成了两行?
那是第 3 条记录的 note,源数据里本来就含一个换行符。按 RFC 4180,字段内换行是合法的,只要整格用双引号包起来,解析器就知道这两行属于同一条记录。Excel、Numbers、pandas 都能正确读回成一条记录。如果你看到的是断成两条记录,说明用的解析器不支持带引号的字段内换行,这种实现不完整。
相关工具
- CSV格式化:已经拿到 CSV 之后对齐列宽、统一换行符,和本页是前后接力。
- TSV转CSV转换器:分隔符层面的互换,注意 TSV 里的制表符不能出现在字段内部。
- CSV转Markdown表格:把同一份数据变成能直接贴进文档或 README 的表格。
- JSON转YAML转换器:保留嵌套结构的场景,YAML 比 CSV 更适合承载层次。
- JSON转XML转换器:需要重复元素和属性来表达同一批记录时用它。
- JSON行转换器:在数组形式与 JSON Lines 之间互转,适合按行流式处理日志。
- JSON按键排序工具:想让字段顺序统一(例如按字母序)时,先处理源数据再回来转换。
- 开发者工具:查看全部开发者工具。