You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过jq有条件地为匹配/非匹配JSON子树应用不同函数?

jq实现JSON树节点差异化处理:特定路径后代用专属函数,其余节点用另一个函数

需求说明

要给JSON树里的节点分情况处理:如果节点是.deep.path.matching的后代,就执行change_x(把x字段改成"changed");其他所有节点执行delete_y(删除y字段)。用户用YAML读写,但需要jq的实现方案,要求尽量简洁或高效(避免二次遍历)。

示例输入输出

输入YAML

top:
  y: "to-delete"
  x: "keep-original"
deep:
  path:
    matching:
      child1:
        x: "need-change"
        y: "should-remain"
      child2:
        y: "should-remain"
        nested:
          x: "need-change-too"
    non-matching:
      x: "keep-original"
      y: "to-delete"

期望输出YAML

top:
  x: "keep-original"
deep:
  path:
    matching:
      child1:
        x: "changed"
        y: "should-remain"
      child2:
        y: "should-remain"
        nested:
          x: "changed"
    non-matching:
      x: "keep-original"

实现方案

1. 简洁版(优先可读性)

用jq内置的walk遍历所有节点,结合path函数判断当前节点是否属于目标路径的后代。写法简单,适合大多数场景。

# 定义两个处理函数
def change_x: .x = "changed";
def delete_y: del(.y);

# 核心逻辑:遍历每个节点,判断路径后应用对应函数
walk(
  if (path | startswith(["deep", "path", "matching"])) then
    change_x
  else
    delete_y
  end
)

说明:walk会递归遍历JSON树的每个节点,path获取当前节点的路径数组(比如.deep.path.matching.child1的路径是["deep","path","matching","child1"]),startswith判断是否以目标路径前缀开头,然后分别执行函数。唯一缺点是大文档下,每次调用path会重新计算路径,效率略低,但日常使用完全足够。

2. 高效版(优先性能,避免重复计算路径)

手动写递归逻辑,遍历过程中传递当前路径,避免重复计算路径,适合处理大型JSON树。

# 定义处理函数
def change_x: .x = "changed";
def delete_y: del(.y);

# 递归处理函数,参数:当前节点、当前路径数组
def process($path):
  if type == "object" then
    # 判断是否是目标路径的后代
    if $path | startswith(["deep", "path", "matching"]) then
      # 遍历对象的每个键,追加到路径后递归处理,最后应用change_x
      .[] |= process($path + [keys[]]) | change_x
    else
      .[] |= process($path + [keys[]]) | delete_y
    end
  elif type == "array" then
    # 数组元素的路径追加索引,递归处理后应用对应函数
    .[] |= process($path + [index(.)]) | (
      $path | startswith(["deep", "path", "matching"]) ? change_x : delete_y
    )
  else
    # 非容器节点(字符串、数字等),直接判断路径应用函数
    $path | startswith(["deep", "path", "matching"]) ? change_x : delete_y
  end;

# 初始调用,路径为空数组
process([])

说明:手动递归时,每次处理子节点都会把当前路径传递下去,不用重复计算路径,性能比walk版更高。需要分别处理对象和数组的路径追加逻辑,写法稍复杂,但大文档下优势明显。

使用命令链

因为用户用YAML读写,需要先把YAML转成JSON给jq处理,再转回YAML,比如用yq工具:

yq -o json input.yaml | jq -f script.jq | yq -o yaml -

内容的提问来源于stack exchange,提问作者yaccob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 00:42:25