日常开发中总跟 JSON 打交道:看日志里的单行 JSON 看着眼瞎,找两个 API 返回的差异找到眼花,把 JSON 转成 CSV 给运营同事折腾半天,还有那些带转义的字符串拷来拷去……后来我自己写了一套命令行脚本,把四个常用功能集成到一起。这篇文章不讲复杂理论,直接给命令、给例子、给踩坑记录。
一、为什么要自己折腾 JSON 工具
- 推荐在线json工具https://www.viddown.cn/tools/json/editor/
以下是离线版工具 - 命令行里处理 JSON,用
jq能格式化,但对比差异、转换格式还得翻文档。 - 批量处理几十个 JSON 文件,用 GUI 点来点去不如一行脚本。
所以我自己维护了一个 json-tools 目录,里面放了几个脚本和一个 jq 的常用命令速查。
二、工具清单
| 功能 | 命令行实现 | 依赖 |
|---|---|---|
| 格式化 / 美化 / 压缩 | jq |
jq |
| 差异对比 | jd 或 diff <(jq . a.json) <(jq . b.json) |
jd / diff |
| 格式转换(JSON ↔ CSV / XML / Python dict) | jq + 内置脚本 |
jq, Python |
| 字符串转义 / 反转义 | python -c "import json; print(json.dumps/loads(...))" |
Python |
三、安装依赖(老生常谈)
Linux (Ubuntu/Debian)
sudo apt update
sudo apt install jq
# 差异对比工具 jd(更直观)
wget https://github.com/josephburnett/jd/releases/download/v1.8.0/jd-amd64-linux
chmod +x jd-amd64-linux
sudo mv jd-amd64-linux /usr/local/bin/jd
macOS
brew install jq
brew install jd
Windows
可以用 choco install jq,或者用 WSL。我一般直接在 WSL 里跑。
验证安装:
jq --version
jd --version
四、基础用法:每个功能怎么用
4.1 JSON 格式化 / 美化 / 压缩
美化(缩进 2 空格,排序 key):
cat ugly.json | jq '.' > pretty.json
压缩(去空格、换行):
cat pretty.json | jq -c '.' > minified.json
从 API 直接格式化:
curl -s https://api.example.com/data | jq '.'
踩坑:如果 JSON 里有不合法的 NaN 或 Infinity,jq 会报错。标准 JSON 不支持这些,需要先用 sed 替换成 null 或字符串。
4.2 JSON 差异对比
方法一:用 jd(推荐,彩色输出)
jd a.json b.json
输出类似:
diff
- ["name"]: "Alice"
+ ["name"]: "Bob"
方法二:用 jq + diff(系统自带)
diff <(jq -S . a.json) <(jq -S . b.json)
-S 对 key 排序,避免顺序不同误报差异。
踩坑:如果 JSON 很大,diff 输出不好读。jd 支持深度限制:jd -depth 2 a.json b.json,只看顶层差异。
4.3 格式转换
JSON ↔ CSV(适合表格数据)
假设有一个 users.json:
json
[{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
转 CSV:
jq -r '.[] | [.name, .age] | @csv' users.json > users.csv
CSV 转 JSON(稍微复杂,用 Python 更稳):
python -c "import csv, json, sys; print(json.dumps(list(csv.DictReader(sys.stdin))))" < users.csv
JSON ↔ Python dict(用 Python 的 ast.literal_eval,常用于复制粘贴)
echo "{'name': 'Alice'}" | python -c "import sys, json, ast; print(json.dumps(ast.literal_eval(sys.stdin.read())))"
JSON ↔ XML(需要第三方工具 yq,它是 jq 的 XML 版)
安装 yq(支持 XML):
pip install yq
转 XML:
yq -x . input.json > output.xml
4.4 JSON 字符串转义 / 反转义
场景:日志里有一行 {"msg": "{\"a\":1}"},需要提取内部的 JSON。
反转义(把 \" 变成 ",然后解析):
echo '{"msg": "{\"a\":1}"}' | jq -r '.msg' | jq '.'
转义(把普通字符串转成可嵌入 JSON 的转义形式):
echo 'Hello "world"' | python -c "import sys, json; print(json.dumps(sys.stdin.read()))"
输出 "Hello \"world\"\n"。
更好的命令行工具:jq -R -s '.' 也可以对字符串进行 JSON 编码。
echo 'Hello "world"' | jq -R -s .
踩坑:jq -R(raw input)会把每行作为字符串,-s 把整个输入当作一个字符串。不加 -s 会每行输出一个 JSON 字符串。
五、实战:组合使用解决实际问题
- 格式化 + 对比两个 API 响应
curl -s api1 > /tmp/a.json
curl -s api2 > /tmp/b.json
jq . /tmp/a.json > /tmp/a.pretty
jq . /tmp/b.json > /tmp/b.pretty
jd /tmp/a.pretty /tmp/b.pretty
- 从日志中提取 JSON 字段并转 CSV
假设日志每行是 2025-01-01 {"user": "alice", "score": 100}:
grep "user" app.log | cut -d' ' -f2 | jq -r '[.user, .score] | @csv' > report.csv
- 批量处理目录下所有 JSON 文件(压缩 + 备份)
for f in *.json; do
jq -c . "$f" > "min/${f}"
done
- 把环境变量转成 JSON(比如配置注入)
env | grep "APP_" | jq -R 'split("=") | {key: .[0], value: .[1]}' | jq -s 'from_entries'
六、问题汇总(都是我真实遇到的)
-
jq: error: Cannot index number with string
原因:对非对象类型使用了 .field 语法,比如 jq '.name' 但输入是数组或数字。
解决:先查看数据结构 jq '.',确认路径存在。 -
jd 对比时提示 input not a valid YAML/JSON
原因:文件编码问题(比如带 BOM),或者末尾有逗号。
解决:用 jq . file > fixed 重新生成一遍,jq 会校验并输出标准格式。 -
JSON 转 CSV 时字段缺失导致列不对齐
原因:有些对象缺少某些字段,@csv 会输出空字符串,但列顺序是固定的。
解决:先用 map 补齐缺失字段:
jq '[.[] | {name: .name, age: (.age // "unknown")}] | .[] | [.name, .age] | @csv' data.json
-
字符串转义后换行符变成了 \n 字面量
场景:想把多行文本塞进 JSON,结果 Hello\nWorld 在 JSON 里变成 Hello\nWorld。
原因:双重转义。
解决:使用 jq -R -s 会正确保留换行符为 \n。 -
diff <(jq . a) <(jq . b) 在 macOS 上报错
原因:macOS 的 diff 不支持进程替换 <(...),但 zsh 支持。
解决:改用 bash 执行,或者先输出临时文件。 -
大文件(几百 MB)用 jq 格式化会卡死
原因:jq . 会把整个文件读入内存并格式化,大文件会 OOM。
解决:使用 jq --stream 流式处理,或者只提取需要的字段:
jq -c '.field' huge.json > extracted.json
七、一点小建议
把常用命令写成 shell 函数放 .bashrc,例如:
jsonfmt() { jq '.' "$1" | bat -l json; }
jsondiff() { jd "$1" "$2"; }
jsoncsv() { jq -r '.[] | [.key1, .key2] | @csv' "$1"; }
对于 JSON 表达式测试,可以用 jq -n '{a:1,b:2}' 快速尝试语法,不需要临时文件。
遇到复杂的 JSON 处理(嵌套分组、聚合),先看 jq 的 group_by、reduce 函数,比写 Python 快。
永远不要在生产环境直接 curl | jq > 覆盖原文件。先写到临时文件,确认无误再 move。
最后推荐一个我每天用的 alias:
alias jq-pretty='jq . -S | bat -l json'
输出带语法高亮,养眼多了。
JSON 工具没有银弹,但 jq + jd + 一行 Python 能覆盖 90% 的需求。剩下的 10%,交给 Stack Overflow。
写完这篇,我把上面四个功能做成了四个独立脚本,丢在 ~/bin/ 下:
json-fmt
json-diff
json-convert
json-escape