前几天调一套数据处理流程,脚本跑一半直接抛异常:
failed to deserialize the json body into the target type: input: missing field这是典型的“某一行 JSON 数据和目标字段模型对不上”。我打开那个 .jsonl 数据集文件想快速定位是哪一行、缺了哪个字段,结果编辑器的状态栏显示这一行有 18 万字符,光标一移过去,差点把笔记本卡死。
我下意识点开浏览器收藏夹里的“JSON 工具”文件夹,里面有 8 个工具,攒了快三年:两个在线格式化网站、一个在线转 CSV 的小站、一个 JSON 可视化编辑器、一个 Notepad++ 插件、一个 Chrome 扩展、一个 Postman,还有一个我写了半截的 Python 处理脚本。挨个试了一圈,没有一个能干净利落地告诉我,就是从第 43 行开始,里面少了 text 字段。
最后真正救火的是一个我一直没当回事的命令行工具——jq。一条命令下去,问题行全被筛出来了。后面我会详细讲它怎么做到这一步,也会把 8 个被淘汰的工具挨个拆一遍,给同样被 JSON 折腾过的人一个参考。
1. 先说说我收藏夹里那些工具是怎么来的
1.1 我日常主要和哪种 JSON 打交道
我的工作经常要处理数据集,尤其是一种叫 JSON Lines(简称 .jsonl)的格式。它和普通 JSON 数组不一样,不是从[开始、到]结束、中间一堆逗号的那种大块头,而是每一行都是一个独立的 JSON 对象,行与行之间没有逗号,加载时可以逐行读取,不用把整个文件一次性吞进内存。
这种格式在数据采集、清洗、模型训练里非常常见。比如 HuggingFace 的datasets库,导入本地 JSON 文件时通常就是.jsonl格式;很多 API 做日志导出,也是一行一个 JSON 事件。我这边一个中等规模的数据集,几百 MB 到几个 GB 都是常事,里面每条数据大概长这样:
{"id": 1, "text": "今天天气不错", "labels": ["天气", "生活"], "meta": {"source": "web", "ts": 1700000000}} {"id": 2, "text": "某电商订单异常", "labels": ["电商"], "meta": {"source": "app", "ts": 1700000060}} {"id": 3, "text": "退款流程咨询", "labels": ["客服"], "meta": {"source": "chat", "ts": 1700000120}}所以我对 JSON 工具的需求很明确:格式化、校验、压缩、抽取字段、过滤数据、转换格式,最好是命令行里一条命令能搞定,还能塞进 Shell 脚本里跑。功能不全的工具对我来说就是鸡肋。
1.2 八年工具的敝帚自珍史
收藏夹里的 8 个工具不是一天攒出来的,是我在无数个临时需求下,今天看到一个“好像有用”就收藏,明天遇到一个“应该能用上”就安装,慢慢堆出来的。
最早收藏的是在线格式化网站,因为那时候我对 JSON 的印象就是“一串乱七八糟的字符串”,扔进网页里点一下按钮就能变整齐,特别香。后来做数据采集,需要把 JSON 转成 CSV 给同事用,又收藏了一个转换网站。再后来项目规模变大,在线工具上传几 MB 的文件就开始卡,我转而装了 Notepad++ 的插件、Chrome 浏览器扩展,甚至买过一个月 Postman,就为了里面那个 prettify 按钮。越攒越多,但真遇到问题的时候,它们反而帮不上忙。
我这段时间认真复盘过这件事:工具多不代表效率高。真正的分水岭不是“能格式化”,而是“能不能和我的工作流融为一体”。收藏夹里的工具多数是一次性的,格式化完就关掉,下次再用还要重新打开、复制、粘贴,做不了任何批量处理。而 jq 是那种完成格式化之后,还能继续帮你查、帮你过滤、帮你转格式的工具。收藏夹里那 8 个,本质上都只解决了“看”这一步,jq 解决的则是“处理”全流程。
2. 被替换掉的 8 个工具,逐个说清楚败在哪
2.1 在线派:方便归方便,但只适合偶尔用一次
在线格式化网站,典型代表是 JSON.cn、BeJSON 这类。它们的优点是零安装、打开就能用,对纯新手友好。但用久了你会发现问题很明显。
第一是隐私风险。你手里的数据可能是内部业务数据,甚至包含用户手机号、地址。往第三方网站粘贴一份,等于把数据送出去了一次。我这里有段时间上传日志做格式化,后来被安全同事提醒,才知道公司对这些在线工具是明令禁止的。从那以后,涉及业务数据的 JSON 我根本不敢上传。
第二是效率低下。浏览器开网页、粘贴数据、等待格式化、复制结果,这套动作看起来只有几秒钟,但如果一天要处理几十次,时间成本就很吓人。更别提大文件。超过 5 MB 的 JSON 放进在线工具,浏览器直接假死,页面跳到“无响应”,有时候连数据都找不回来。
还有一个在线 JSON 转 CSV 的小工具,问题更突出。它只能处理“数组里面一层对象”这种最简单的情况。一旦对象里嵌套了数组、字典,转换结果直接乱套,列对不上、值丢了。我刚开始以为是操作问题,后来发现是工具本身就没考虑嵌套结构。它适合应付一次两次的临时需求,根本扛不住真实数据。
2.2 本地派:装了不代表会用,用了不代表能融入工作流
本地工具这块我攒了四样:JSON Editor Online 可视化编辑器、Notepad++ 的 JSON Viewer 插件、Chrome 的 JSON Formatter 扩展,还有 Postman。
JSON Editor Online 是个可视化编辑器,左侧是原始的 JSON 文本,右侧是树形结构,可以展开、折叠、增删字段。它确实漂亮,第一次用的时候我惊艳了好一阵。但问题是太重了,处理一个小 JSON 也要开网页、等待加载,而且它对大批量数据支持一般,一旦文件到了几十 MB,右侧树形结构渲染直接卡成幻灯片。说白了,它是一个“给人看”的工具,不是“给数据流水线用”的工具。
Notepad++ 插件的典型缺点是平台锁定。它只能在 Windows 上用,而我后来工作环境切成 macOS,这个插件就彻底废弃了。Chrome 扩展也是这样,它只是让浏览器里打开的 JSON 地址显示得更整齐,遇到本地文件、管道输出、服务器响应,完全用不上。Postman 就更不用说了,它本质上是 API 调试工具,不是 JSON 处理工具。为了一个格式化功能,每次要等它启动、加载工作区,杀鸡用牛刀,而且牛刀还不好使。
这些工具各自都很精致,但它们解决的是同一个问题:把 JSON 变好看。一旦问题变成“找出缺字段的行”“统计某个字段的分布”“把这几万条数据里的 email 抽出来”,它们集体哑火。
2.3 脚本派:自己动手虽好,但维护成本全在自己身上
程序员碰上 JSON 问题,最容易想到的就是写脚本。我以前也是这么干的,而且不止一次。最轻量的是python -m json.tool,它能在命令行里格式化 JSON,确实是个好东西。但它的功能很单一,老老实实格式化可以,想过滤、想提取、想转换,都要加参数、写 Python 代码,等于绕了一圈回到原始时代。
更失控的是我的自写脚本。最初只是写几行 Python 处理单个 JSON 文件,后来需求越来越复杂:要遍历 JSONL、要处理缺失字段、要按条件过滤、要输出 CSV……脚本越改越长,还要处理各种边界情况,最后变成了一个我没时间维护的“项目”。有一次需求变了,我改了一下午,越改越乱,最后怒删脚本。
那个下午之后我意识到,很多“自写脚本”本质上是拿自己当开发资源去填工具的天坑。如果有一个现成工具能覆盖八成场景,剩下两成用脚本补充,根本没必要从零开始造轮子。
2.4 8 个工具的横向对比
| 工具 | 类型 | 主要用途 | 被替换原因 |
|---|---|---|---|
| JSON.cn / BeJSON | 在线网站 | 格式化、压缩 | 隐私风险、大文件卡顿、不能批量 |
| 在线 JSON 转 CSV 小站 | 在线网站 | JSON 转 CSV | 嵌套结构支持差、数据需上传 |
| JSON Editor Online | 网页可视化编辑器 | 树形编辑、格式化 | 交互重、大文件卡顿、不可脚本化 |
| Notepad++ + JSON Viewer | 桌面插件 | 格式化、查看 | 仅 Windows 可用、跨平台不便 |
| Chrome JSON Formatter | 浏览器扩展 | 美化浏览器中的 JSON | 局限于浏览器、不能处理本地文件 |
| Postman | 桌面客户端 | API 调试、JSON 美化 | 启动慢、功能太重、不适合流水线 |
| python -m json.tool | 命令行工具 | 格式化 | 功能单一、查询过滤仍需写代码 |
| 自写 Python 脚本 | 脚本 | 处理特定数据场景 | 维护成本高、改需求像开新项目 |
这份表格列下来,你会发现它们的共同问题:没有一个是全能型的。要么只能格式化,要么只能看不能处理,要么功能全但太重。我需要的不是一个“JSON 显示美化器”,而是一个能嵌入命令行、能批量处理、能查能改能转换的 JSON 处理器。
3. 只留下这一个:jq,一个命令解决 90% 的 JSON 问题
3.1 为什么偏偏是 jq
jq 是一个命令行 JSON 处理器,官网叫 jqlang.github.io/jq。它用 C 语言写的,发布形式是单个可执行文件,Linux、macOS、Windows 都有对应版本。不需要装运行时,不需要依赖什么库,下载下来就能跑。
我最初对 jq 的印象是“一个玩具级别的工具”,只会在终端里执行jq .然后看 JSON 变整齐。后来被那 8 个工具轮番折磨之后,我才静下心去翻 jq 的文档,发现它远比我想象的强。它支持字段提取、数组过滤、对象构造、条件判断、字符串操作、正则匹配、流式处理,基本上能覆盖我日常 90% 的 JSON 操作。
它最大的特点是“管道式设计”。在 Unix 环境里,命令之间可以用管道符|串联,jq 天然就是为管道而生的。cat 数据.jsonl | jq '.text'这是很自然的组合,不需要打开任何 GUI,不需要切窗口,一条指令输出结果到屏幕,也可以重定向到文件,方便后续继续处理。
3.2 格式化与校验:从我的翻车现场说起
回到开头那个报错。failed to deserialize the json body into the target type: input: missing field,这类错误通常意味着某行数据和目标模型对不上。我当时用的 Python 模型大概长这样:
from pydantic import BaseModel class Sample(BaseModel): id: int text: str labels: list[str] = []目标模型要求每行数据必须有text字段,但数据集里混进了一些脏行。用 jq 定位脏行特别直接,一条命令:
jq -c 'select(has("text") | not)' dataset.jsonl-c是让输出保持单行紧凑,select(...)是做条件过滤,has("text") | not表示“不包含 text 字段”。运行完,所有缺少 text 的行都会被打印出来。配合 jq 1.7 提供的input_line_number,还可以直接输出原始行号:
jq -c 'select(has("text") | not) | {line: input_line_number, id: .id, keys: keys}' dataset.jsonl我当时的输出长这样:
{"line": 43, "id": 42, "keys": ["id", "labels"]} {"line": 117, "id": 118, "keys": ["id", "labels", "category"]}瞬间就知道问题在哪了。如果只是单纯校验 JSON 是否合法,jq 也有标准做法:
jq -e . dataset.json > /dev/null && echo "valid" || echo "invalid"-e会在输入不是合法 JSON 时返回非零退出码,配合&&和||就能判断。对 JSONL 文件逐行校验也一样,jq -e . dataset.jsonl > /dev/null,如果第一行开始就报错,说明文件开头就不对;如果跑完没有输出,说明整体没有语法错误。
这里有个很实用的点:校验和格式化是 jq 最不起眼的能力。很多人只把它当 prettify 用,但其实这两步只是入口。真正让它不可替代的,是下面的查询与过滤。
3.3 查询与过滤:替代临时写 Python 脚本的日常操作
提取字段是最高频的场景。一个 JSON 对象,只要取其中几个字段:
jq '.user.name' user.json嵌套字段用点号连接,数组用下标。提取数组里每个对象的某个字段,写法是这样:
jq '.items[] | .name' data.json这里的.items[]会把 items 数组“展开”成一个个元素,然后逐个取.name。如果你的数组很大,还可以继续过滤,比如只取价格为 100 以上的商品:
jq '.items[] | select(.price > 100)' data.json我在没完全掌握 jq 之前,这种需求会写 Python:
import json with open("data.json", encoding="utf-8") as f: data = json.load(f) filtered = [item for item in data["items"] if item["price"] > 100] print(json.dumps(filtered, ensure_ascii=False))为了一个过滤逻辑,开文件、读内容、写循环、输出结果,这套动作重复多了真的会烦躁。用 jq 就是一行命令的事,还能继续往下接管道,比如统计过滤后还剩多少条:
jq '[.items[] | select(.price > 100)] | length' data.json构造新对象也很常用。原始数据有一堆字段,我只想保留其中几个,并加一个计算字段:
jq '.items[] | {name: .name, final_price: (.price * (1 - .discount))}' data.json输出结果就是每个商品一行 JSON,结构干净整齐。对于 JSONL 文件,配合-c输出,每行一个结果,可以直接喂给下一个处理步骤。
3.4 压缩、修改与转换:当成数据处理流水线的标准件
压缩也是 jq 的拿手好戏。格式化是往复杂里变,压缩是往精简里变,命令是jq -c . file.json。这个命令会把原本多行缩进的 JSON 压成一行,体积小,方便存日志、传消息队列、或者粘贴到一些只接受单行 JSON 的接口里。
修改 JSON 结构这件事,jq 也能做。比如我想给数据集里每条数据加一个timestamp字段:
jq '. + {ts: 1700000000}' dataset.jsonl或者批量把status字段从"old"改成"new":
jq 'map(.status = "new")' data.json需要说明的是,jq 不会原地修改文件,它只会把修改后的内容输出到标准输出。想真正覆盖原文件,需要重定向到临时文件再移动回来:
jq 'map(.status = "new")' data.json > /tmp/data.json && mv /tmp/data.json data.json转换格式是另一个高频需求。把 JSON 数组转成 CSV,jq 一行就能搞定:
jq -r '.items[] | [.name, .price, .category] | @csv' data.json-r表示裸输出,不加 JSON 字符串的引号;@csv会把数组变成一行逗号分隔的文本,自动处理转义。结果长这样:
"商品A",199,"数码" "商品B",399,"家电"配合输出重定向,就能直接生成 CSV 文件:
jq -r '.items[] | [.name, .price, .category] | @csv' data.json > result.csv同样的思路,@tsv可以转成制表符分隔的格式。这个能力在过去我要写十行 Python,现在一秒钟完成。
3.5 文件大了怎么办
在线工具处理大文件会卡死,jq 在这方面强得多。前面提过,我手里有几百 MB 甚至几个 GB 的 JSONL 数据,jq 跑过滤和提取都能扛住。普通文件加载进内存处理没问题,但如果你要处理的是超大单文件,比如几个 GB 的 JSON 数组,jq 也提供了流式模式:
jq --stream 'select(.[0] | contains(["text"]))' big.json流式模式把 JSON 拆成一串路径和值的组合,逐个处理,不会一次性加载整个数据结构进内存。不过说实话,流式模式的表达式写起来稍微费脑,日常用到的机会不算多。大多数场景下,几百万行的 JSONL 数据用jq 'select(...)'直接跑,性能完全够。
实测下来,一个 400 MB 左右的 JSONL 文件,做字段过滤加提取,耗时在几秒到十几秒之间。考虑到数据量,这个速度我很满意。而且 jq 是原生 C 编译的程序,启动几乎没有延迟,不像图形工具又要打开界面又要加载数据。
4. 从收藏到真正会用:jq 避坑清单
4.1 新手最容易碰的语法问题
用 jq 最容易踩的坑,第一个是忘了加-r。默认情况下,jq 输出的字符串是带 JSON 引号的。比如执行jq '.name' user.json,结果可能是"张三",而不是张三。如果你想到文件里,想要的是纯文本,就必须加-r:
jq -r '.name' user.json第二个坑是单引号和双引号的问题。在 Shell 里,jq 表达式一般用单引号包起来,比如jq '.a.b'。因为表达式里有$、空格、括号之类的特殊字符,双引号会被 Shell 先解释一轮,很容易出问题。我见过不少人在 Windows 的 cmd 或 PowerShell 里折腾半天,后来换了单引号就好了。
第三个坑是混淆.foo和.foo[]。.foo取的是数组整体,.foo[]是展开数组里的每个元素。如果数组嵌套了一层,想要每个子对象里的字段,就必须写.foo[].bar。结构不对,输出结果会差很多。
第四个坑是 jq 用length获取对象数量或字符串长度,但它对不同类型的行为不一样。数组是元素个数,字符串是字符个数,对象是键的个数。指望一个length走天下,可能算出来和你预期不一样。
4.2 编码、精度与 Windows 环境的坑
中文编码问题,几乎是必踩的坑。默认情况下,jq 转成 JSON 字符串时会把非 ASCII 字符转义成\uXXXX,比如“中文”会变成"\u4e2d\u6587"。这样虽然安全,但不方便阅读。想保留原文,加一个--unicode-output参数,或者直接在命令里传-u:
jq -u '.text' dataset.jsonl另一个容易忽略的坑是长整数的精度问题。JSON 里的数字,jq 底层使用双精度浮点数表示,超过 2 的 53 次方的整数(比如雪花 ID、某些 64 位大整数)会被截断,导致精度丢失。你处理包含超大 ID 的数据时,如果发现 ID 尾巴变成 0,别觉得是数据问题,多半是 jq 的数值精度上限到了。这种情况我的建议是:如果对这些 ID 只是原样透传,不要用 jq 输出 JSON 格式,尽量用-r转成字符串;如果要做复杂处理,改用 Python 的int类型更稳。
Windows 环境还有个容易忽略的问题:PowerShell 默认的输出编码可能不是 UTF-8,导致中文乱码。我建议 Windows 上优先用 Git Bash 或者 WSL 来跑 jq,省去一堆编码和引号相关的折腾。安装的话,Windows 可以用winget install jqlang.jq或者scoop install jq,macOS 用brew install jq,Linux 用包管理器装就行。
4.3 哪些场景还是别硬用 jq
jq 很强,但也不是万能的。遇到下面几种情况,我建议你可以放弃 jq,老老实实回到 Python 或其他专用工具。
一种是复杂的 JSON Schema 校验。jq 能做存在性判断,可以做条件过滤,但如果你想校验字段类型、枚举值、对象结构是否完全符合某个规范,那就不是 jq 擅长的领域了。这类需求适合用 JSON Schema 校验库,比如 Python 的jsonschema。
一种是超大数据规模的频繁交互。jq 的单次查询性能很好,但不适合交互式浏览。如果你需要像操作 Excel 一样频繁展开、折叠、编辑 JSON,用 JSON Editor Online 或 VS Code 的 JSON 插件体验会好很多。工具不是越高级越好,合适才是硬道理。
还有一种是你需要把逻辑封装成可复用的业务代码。jq 表达式写出来很爽,但它不是一种通用编程语言,写复杂分支和循环会很别扭。如果你的处理逻辑超过十行,后续还要被别人维护,用 Python 或者 Go 写成一个正式的小服务,会是更合理的选择。
写在最后
我现在收藏夹里已经空了一大半,只留了几个真正高频的工具,jq 是其中之一。说实话,刚开始学 jq 的时候,我也觉得它语法奇怪,记不住.foo、.foo[]、select这些写法。但坚持用了两周之后,它已经成了我肌肉记忆的一部分。每次在终端里敲出jq开头的命令,我都觉得比点开收藏夹里任何图形界面工具要踏实。
如果让我给一个具体建议,那就是不要把它当成一个“格式化工具”来学,而是当成“JSON 的水管工”:让它帮你从 JSON 里取数、过滤、变形、连接下一个命令。收藏夹里的工具少了,解决问题的方法反而多了。最后再多说一句,如果手头有一批 JSONL 数据要处理,花一个小时把jq -r、select、map、@csv这几个基础操作练熟,回报率高得惊人。同一个坑,踩过一次就长记性,但没必要在 8 个工具里反复踩。