跳到主要内容

gron 拍平 JSON:让深层数据可以被 grep 的赋值语句格式

深层 JSON 难 grep 难定位路径,gron 把它拍平成每行一条带完整路径的赋值语句,grep 一搜就到。本文讲它的语法、真实输入输出、怎么反转回 JSON,以及命令行调试 API 响应的实战。

发布于 作者 李雷
#json #gron #命令行 #调试 #grep

gron 拍平 JSON:让深层 JSON 可以被 grep

接口返回一份四千行的 JSON,我要的只是某处埋着的 refresh token。在编辑器里折叠展开、顺着缩进一层层下钻,翻了两分钟还没找到,这种场景我遇到过太多次。问题不在我手慢,而在 JSON 这种嵌套结构天生不适合 grep:一个值的"名字"和它的"位置"分散在好几行里,grep 一个键名只能告诉你这个键存在,告诉不了你它在哪条路径上、值是多少。

gron 就是冲着这个痛点来的。它把一份 JSON 改写成每行一条赋值语句,左边是从根到这个值的完整路径,右边是 JSON 字面量,像这样:

json.users[0].name = "Ada";

这一行自己就说清了三件事:这是第一个 user,它的字段叫 name,值是 Ada。grep 一搜,路径和值一起跳出来。

为什么深层 JSON 难 grep

grep 是按行工作的。一份格式化过的 JSON 里,键和值常常不在同一行,数组下标更是完全不出现在文本里。你 grep 一个 email,grep 只会把那一行 "email": "a@b.com" 还给你,但它属于哪个对象、是数组里第几个元素、在哪条路径下,grep 全不知道。要回答这些,你得回到文件里手动数缩进,或者写一个 jq 过滤器。

jq 当然能干这事,但你得先知道结构长什么样才写得出过滤器,而你 grep 的初衷恰恰是因为还不知道结构。这就成了死循环。gron 把循环打开:它先把结构摊平成文本,让 grep 这种最朴素的工具就能直接用。

gron 的语法:每个值一行,带完整路径

gron 的规则很少,读几行就懂:

  • 对象的键用点号连接:json.config.timeout
  • 数组元素用方括号下标:json.items[0]json.items[1]
  • 不是普通标识符的键写成括号引号:json["weird key"]
  • 字符串保留引号:= "hi";;数字、布尔、null 裸写:= 42;= true;= null;
  • 每个容器(对象、数组)也单独留一条声明行,所以结构完整可还原

正因为每个值都带着自己从根算起的绝对路径,你可以直接 grep 路径里的任意一段,也可以 grep '\[3\]' 只看第四个数组元素,或者 grep -c 数一下匹配条数。

一份真实的 JSON 转成 gron 长什么样

拿一段配置举例。原始 JSON:

{
  "service": "api",
  "replicas": 2,
  "ports": [8080, 8443],
  "auth": { "token": "eyJabc", "enabled": true }
}

转成 gron 之后,每个叶子各占一行:

json = {};
json.service = "api";
json.replicas = 2;
json.ports = [];
json.ports[0] = 8080;
json.ports[1] = 8443;
json.auth = {};
json.auth.token = "eyJabc";
json.auth.enabled = true;

现在想找 token?grep token 直接给你 json.auth.token = "eyJabc";,路径、值、类型一目了然,不用展开任何嵌套。想知道开了几个端口?grep 'ports\[' 就把两个端口连同下标列出来。

反转回 JSON:ungron

gron 不是单向的。ungron 是反方向:它读这些赋值语句,沿着每条路径按需创建对象和数组,把字面量放回对应位置,精确重建出原来的 JSON,往返不丢信息。

这带来一条很顺手的工作流:gron 拍平 → 改那一行 → ungron 还原。比如要把 json.config.replicas 从 2 改成 5,你不想在三层嵌套里手动定位,那就先 gron,用文本编辑器或 sed 改那一行,再 ungron 重建成合法、带缩进的 JSON。路径语法让这次修改毫无歧义,往返过程保证结构正确。

有一点别踩坑:gron 的输出不是合法 JSON,它是一串赋值语句,别直接喂给 JSON 解析器。要拿回 JSON,记得把方向切到 ungron。

命令行调试 API 响应的实战

我最常用它的场合是调接口。接口返回一坨我没见过的 JSON,我把响应跑一遍 gron,然后 gron resp.json | grep -i token,名字里带 token 的每条路径连同值全列出来,json.data.session.refresh_token = "eyJ…"; 一眼就到。同样的招数找 URL、找 id、找邮箱都成立:grep 关键词,读路径,搞定。

diff 两份 JSON 也很受用。直接 diff 结构化 JSON 噪声大,键顺序一动位置全变,重排过的对象看起来像上百行改动。两份都 gron 之后是扁平、稳定的"路径 = 值"行表,普通的逐行 diff 就只把真正变了的叶子拎出来。

浏览器里就能跑这套转换,不用装 CLI:打开 gron 转换器,粘 JSON,切到 JSON 转 gron,复制那些行到 grep 管道里直接用;要还原就切 ungron。整个过程在浏览器本地完成,JSON 不上传。如果你只是想把 JSON 拍成扁平的点号键映射表而不是赋值语句,可以看看 JSON Flatten,它和 gron 的侧重点不同:flatten 给一张扁平映射,gron 为命令行 grep 和往返重建而生。

第一次用 gron 时我有点怀疑,觉得不就是换个格式。真上手才发现,把"位置"写进每一行这件事,彻底改变了我读陌生 JSON 的方式:不再脑补缩进,grep 一下结构就摊在眼前。


Made by Toolora · Updated 2026-06-13