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

使用JQ递归移除空数组与空对象对应键的问题排查

问题描述

输入JSON:

{
  "a": {
    "a": "foo",
    "b": "bar"
  },
  "b": {
    "a": [],
    "b": []
  },
  "c": {},
  "d": [
    "foo",
    "bar"
  ]
}

期望结果:递归移除所有指向空数组或空对象的键,得到:

{
  "a": {
    "a": "foo",
    "b": "bar"
  },
  "d": [
    "foo",
    "bar"
  ]
}

尝试的JQ命令:

jq 'walk(if ((type=="object" or type=="array") and length==0) then empty else . end)'

错误结果:仅保留最后一个数组:

{
  "d": [
    "foo",
    "bar"
  ]
}

将命令中的empty替换为{"A":1}后:

jq 'walk(if ((type=="object" or type=="array") and length==0) then {"A":1} else . end)'

得到符合预期的中间结果(非空对象未被修改):

{
  "a": {
    "a": "foo",
    "b": "bar"
  },
  "b": {
    "a": {
      "A": 1
    },
    "b": {
      "A": 1
    }
  },
  "c": {
    "A": 1
  },
  "d": [
    "foo",
    "bar"
  ]
}
问题原因分析

核心问题在于empty的特殊性和walk函数的执行逻辑:

  1. walk的执行顺序是先递归处理所有子元素,再对当前元素应用过滤函数。
  2. 当你用empty替换空数组/空对象时,对象中值为empty的键会被直接丢弃。比如处理b对象的子键a和b(都是空数组)时,它们会被替换为empty,导致b对象失去所有子键,变成空对象。
  3. 这个新生成的空对象会再次被过滤函数检测到,符合type=="object" and length==0的条件,进而被替换为empty,最终导致顶层的b键被移除。
  4. 若你的非空对象(如a)也被误删,大概率是命令逻辑存在疏漏(比如条件判断错误)或使用了旧版本jq的walk实现,但从替换empty为{"A":1}的测试结果来看,a对象本身是符合保留条件的,误删可能是测试过程中的操作失误。
正确解决方案

要实现递归移除空数组/空对象的键,需要调整逻辑:只清理对象中值为空的键,而非直接替换空结构为empty。推荐使用以下命令:

jq 'walk(
  if type == "object" then
    with_entries(select(.value | (type != "array" and type != "object") or length > 0))
  elif type == "array" then
    map(select((type != "array" and type != "object") or length > 0))
  else
    .
  end
)'

逻辑说明

  • 处理对象时:通过with_entries(select(...))筛选出值不是空数组/空对象的键;
  • 处理数组时:通过map(select(...))保留非空结构的元素;
  • 非对象/数组的元素直接保留,不做修改。

执行该命令后,会得到你期望的结果:

{
  "a": {
    "a": "foo",
    "b": "bar"
  },
  "d": [
    "foo",
    "bar"
  ]
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:59:54