使用 jq 转换和构建 JSON 对象
使用 map、to_entries 和对象构造来重塑数据,生成新的 JSON 数据。
使用 jq 转换和构建 JSON 对象 是 CoddyKit 上的免费 Linux Command Line & Bash Scripting Mastery 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Linux Command Line & Bash Scripting Mastery 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。
为什么要转换 JSON?
来自 API 或日志文件的原始 JSON 很少会恰好符合您的需求。您可能会收到一个很大的对象,却只想要其中的特定字段;也可能需要重命名键、扁平化嵌套结构,或构建一个全新的负载以发送给其他服务。
jq 是一款轻量而强大的命令行 JSON 处理器,可在一个流水线中完成这些转换。在本课中,您将学习三种用于重塑数据的核心技术:
- 对象构造 — 从头构建一个新的 JSON 对象
- map — 对数组中的每个元素应用转换
- to_entries / from_entries — 将对象的键值对视为数组,以便过滤并重新构建对象
所有示例均假设已安装 jq(apt install jq / brew install jq)。
对象构造基础
jq 最基础的功能是对象构造:将表达式包裹在 {} 中以构建新的 JSON 对象。您可以选择要包含的字段,并决定它们的名称。
语法:
{ newKey: .existingField }— 重命名字段{ name, age }— 新键与字段名相同时使用的简写形式{ total: (.price * .qty) }— 在行内计算值
下面的代码片段读取产品 JSON,并生成一个更精简的结构,其中包含计算得出的 subtotal 字段。
#!/usr/bin/env bash
# Object construction: pick and rename fields
product='{
"id": 42,
"name": "Widget Pro",
"price": 9.99,
"qty": 3,
"warehouse": "EU-West"
}'
echo "$product" | jq '{
productId: .id,
name,
subtotal: (.price * .qty)
}'从嵌套数据构造对象
现实世界中的 JSON 通常是嵌套的。jq 允许您在对象构造器中访问嵌套路径,同时将结构扁平化。
在构造器的值表达式中使用点号路径表示法:
{ city: .address.city }{ lat: .location.coords.lat }
下面的示例读取一条深度嵌套的用户记录,并生成适合用作 CSV 标题行或 API 请求正文的扁平摘要。
#!/usr/bin/env bash
user='{
"id": "u-001",
"profile": {
"displayName": "Ada Lovelace",
"contact": { "email": "ada@example.com", "phone": "+44-700" }
},
"plan": "pro"
}'
echo "$user" | jq '{
id,
name: .profile.displayName,
email: .profile.contact.email,
plan
}'使用 map 转换数组
map(expr) 相当于 jq 中的逐项处理:它会对输入数组的每个元素应用 expr,并返回一个长度相同的新数组。
要点:
map(expr)是[.[] | expr]的语法糖- 其中的表达式可以是任意 jq 过滤器,包括对象构造
- 可以与
select()连接,在转换前进行过滤
下面的代码片段处理一个订单列表,只保留装运清单所需的字段。
#!/usr/bin/env bash
orders='[
{"orderId": 1, "customer": "Alice", "total": 42.50, "status": "shipped"},
{"orderId": 2, "customer": "Bob", "total": 18.00, "status": "pending"},
{"orderId": 3, "customer": "Carol", "total": 99.99, "status": "shipped"}
]'
# Produce a shipping manifest: only shipped orders, slim fields
echo "$orders" | jq '[
.[] | select(.status == "shipped") | {
id: .orderId,
recipient: .customer,
amount: .total
}
]'使用 map 处理计算字段
在 map 中,您不仅可以复制字段,还可以计算新值、转换类型以及组合字段。常见模式包括:
- 字符串插值:
"\(.first) \(.last)" - 算术运算:
(.price * 1.2 | round),用于加价 20% - 条件表达式:
if .score >= 90 then "A" else "B" end
下面的示例通过添加计算得出的 fullName 和根据工作年限确定的 seniority 标签,丰富员工列表中的数据。
#!/usr/bin/env bash
staff='[
{"first": "Grace", "last": "Hopper", "years": 15},
{"first": "Alan", "last": "Turing", "years": 4},
{"first": "Linus", "last": "Torvalds","years": 9}
]'
echo "$staff" | jq 'map({
fullName: "\(.first) \(.last)",
years,
seniority: (if .years >= 10 then "senior" elif .years >= 5 then "mid" else "junior" end)
})'理解 to_entries
to_entries 会将 JSON 对象转换为由 {key, value} 对组成的数组。这样就可以对对象的字段执行数组操作(map、select、sort),而这些操作无法直接用于对象。
转换示例:
- 输入:
{"a": 1, "b": 2} - 输出:
[{"key": "a", "value": 1}, {"key": "b", "value": 2}]
相反的操作是 from_entries,它会将该数组转换回对象。二者结合后,就形成了对对象执行转换的 to_entries | map(...) | from_entries 惯用模式。
#!/usr/bin/env bash
# Demonstrate to_entries and from_entries
config='{"host": "db.local", "port": 5432, "ssl": true}'
echo "--- to_entries output ---"
echo "$config" | jq 'to_entries'
echo "--- round-trip back to object ---"
echo "$config" | jq 'to_entries | from_entries'使用 to_entries 过滤键
to_entries 最实用的用途之一,是根据键名本身动态过滤要保留或删除的键——当您事先不知道键名时,对象构造无法完成这项工作。
模式:
to_entries | map(select(.key | test("regex"))) | from_entriesto_entries | map(select(.key != "secret")) | from_entries
下面的代码片段会在将配置对象转发给外部服务之前,删除所有以下划线开头的键(内部/私有字段)。
#!/usr/bin/env bash
raw_config='{
"endpoint": "https://api.example.com",
"timeout": 30,
"_internalToken": "s3cr3t",
"_debugMode": true,
"retries": 3
}'
# Remove any key starting with underscore
echo "$raw_config" | jq '
to_entries
| map(select(.key | startswith("_") | not))
| from_entries
'使用 to_entries 动态重命名键
对象构造会在您编写代码时已知键名的情况下重命名键。to_entries 则允许您以编程方式重命名键,例如将 camelCase 转换为 snake_case,或添加前缀。
在 map 中更新每个条目的 .key 字段,然后通过管道传递给 from_entries:
map(.key |= gsub("(?<=[a-z])(?=[A-Z])"; "_") | .key |= ascii_downcase)— 将 camelCase 转换为 snake_casemap(.key |= "app_" + .)— 为每个键添加前缀
该示例会为所有环境变量名添加 APP_ 前缀,以便在注入容器之前为其设置命名空间。
#!/usr/bin/env bash
env_vars='{"host": "localhost", "port": "8080", "debug": "false"}'
# Add APP_ prefix and uppercase all keys
echo "$env_vars" | jq '
to_entries
| map({ key: ("APP_" + (.key | ascii_upcase)), value })
| from_entries
'with_entries:便捷简写
to_entries | map(...) | from_entries 这一模式非常常见,因此 jq 提供了简写形式:with_entries(expr)。
它们完全等价,但后者更简洁:
with_entries(.value |= . * 2)— 将每个数值加倍with_entries(select(.value != null))— 删除值为 null 的键with_entries(.key |= ascii_upcase)— 将所有键转换为大写
下面的代码片段会删除值为 null 或空字符串的所有键,这是向 REST API 发送 PATCH 请求前常见的清理步骤。
#!/usr/bin/env bash
patch_body='{
"name": "Mehmet",
"email": "",
"phone": null,
"city": "Istanbul"
}'
# Drop empty/null fields before PATCH
cleaned=$(echo "$patch_body" | jq '
with_entries(select(.value != null and .value != ""))
')
echo "Cleaned payload:"
echo "$cleaned"
# In practice you would pipe to curl:
# curl -s -X PATCH https://api.example.com/users/1 \
# -H "Content-Type: application/json" \
# -d "$cleaned"在流水线中组合 map 与对象构造
实际的数据转换通常会将多个 jq 操作连接起来。用于准备 API 负载的典型流水线可能会:
- 使用
map(select(...))过滤输入数组 - 使用对象构造重塑每个元素
- 添加计算字段
- 对结果排序
下面的示例读取服务器指标列表,只保留 CPU 使用率较高的服务器,并生成一个可直接 POST 到 webhook 的紧凑告警负载。
#!/usr/bin/env bash
metrics='[
{"host": "web-01", "cpu": 23, "mem": 60, "region": "eu"},
{"host": "web-02", "cpu": 91, "mem": 88, "region": "eu"},
{"host": "db-01", "cpu": 78, "mem": 95, "region": "us"},
{"host": "db-02", "cpu": 12, "mem": 40, "region": "us"}
]'
alerts=$(echo "$metrics" | jq '[
.[] | select(.cpu > 75 or .mem > 85) | {
server: .host,
region,
severity: (if .cpu > 90 or .mem > 90 then "critical" else "warning" end),
metrics: { cpu: .cpu, mem: .mem }
}
] | sort_by(.severity)')
echo "$alerts"从多个来源构建新的 JSON 对象
jq 可以使用加法运算符 +和 as $var 进行变量绑定,合并输入并构建来自多个 JSON 来源的对象。
实用模式:
obj1 + obj2— 合并两个对象(键冲突时以右侧为准)--argjson— 将第二个 JSON 文档作为变量传入$ENV— 直接在 jq 中读取环境变量
下面的代码片段将基础配置与特定环境的覆盖配置合并,这是 shell 脚本中管理十二要素应用配置的常见模式。
#!/usr/bin/env bash
base_config='{
"logLevel": "info",
"timeout": 30,
"retries": 3,
"database": "postgres://db.local/app"
}'
env_overrides='{
"logLevel": "debug",
"database": "postgres://db.staging/app_staging"
}'
# Merge: overrides win on conflicts
merged=$(echo "$base_config" | jq --argjson overrides "$env_overrides" '. + $overrides')
echo "Merged config:"
echo "$merged"知识检查:to_entries 与 map
您有以下 JSON 对象,现在需要删除所有值小于 0的键,生成一个只包含非负值的新对象。哪个 jq 表达式可以正确完成此操作?
输入:{"a": 10, "b": -3, "c": 0, "d": 5}
课程回顾:使用 jq 转换 JSON
您已经学习了使用 jq 重塑 JSON 的基本技术:
- 对象构造
{}— 从输入中选择、重命名和计算字段,以构建新对象 map(expr)— 对数组中的每个元素应用任意转换,包括嵌套对象构造以及用于过滤的select()to_entries/from_entries— 将对象转换为由{key, value}对组成的数组,从而对键和值执行数组操作,然后再转换回对象with_entries(expr)— 完整 to_entries → map → from_entries 流水线的简洁写法- 使用
+和--argjson合并对象,以构建多来源负载
这些基础构件可以组合使用:用 select 过滤,用对象构造重塑,用计算字段丰富数据,并将所有操作连接成一个易读的 jq 表达式。掌握这些模式后,您几乎可以直接在 shell 中处理任何 JSON 负载,而无需用 Python 或 Node 编写专用脚本。
常见问题解答
「使用 jq 转换和构建 JSON 对象」课时是免费的吗?
是的 — 「使用 jq 转换和构建 JSON 对象」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Linux Command Line & Bash Scripting Mastery 课程的其余内容,请升级到 CoddyKit PRO。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。
「使用 jq 转换和构建 JSON 对象」这节课中我会学到什么?
使用 map、to_entries 和对象构造来重塑数据,生成新的 JSON 数据。 你通过在浏览器中直接运行的动手代码来练习 Linux Command Line & Bash Scripting Mastery,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Linux Command Line & Bash Scripting Mastery 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Linux Command Line & Bash Scripting Mastery 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「使用 jq 转换和构建 JSON 对象」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Linux Command Line & Bash Scripting Mastery 课中编写并运行代码吗?
能。每节 Linux Command Line & Bash Scripting Mastery 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 jq 管道筛选和选择 JSON
- 使用 jq 转换和构建 JSON 对象
- 结合 curl 与 jq 调用 REST API
- 使用 yq 编辑 YAML 配置文件