0Pricing
Linux Command Line & Bash Scripting Mastery · 课时

使用 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_entries
  • to_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_case
  • map(.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 负载的典型流水线可能会:

  1. 使用 map(select(...)) 过滤输入数组
  2. 使用对象构造重塑每个元素
  3. 添加计算字段
  4. 对结果排序

下面的示例读取服务器指标列表,只保留 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 jq 管道筛选和选择 JSON
  2. 使用 jq 转换和构建 JSON 对象
  3. 结合 curl 与 jq 调用 REST API
  4. 使用 yq 编辑 YAML 配置文件
← 返回 Linux Command Line & Bash Scripting Mastery