使用jq改造树形JSON:上移单元素列表项、删除空项
需求:用jq处理树形JSON
核心目标
对类文件/文件夹结构的树形JSON执行两类转换:
- 将仅含单个子项的节点从叶子层级上移(删除中间节点,把子项直接放到原父节点下)
- 删除仅包含基础字段的空节点
结构说明
树形结构的组成规则:
- 可选的
.item[]数组:用于存储子节点 - 必选的
.name字段:节点名称 - 可选的
.description字段:节点描述 - 若节点存在除上述字段外的其他额外字段,则该节点不可被删除或合并
示例场景解析
objects/:带有"root f"注释,保留原结构external:无额外字段,但包含2个子项,不执行上移操作external/buuu和external/biii:含额外字段,保留原节点不做处理objects/afolder/methods下的empty和empty2:仅包含name、description或空item数组,属于待删除的空项objects/afolder/methods/partner1/operand:仅含单个子项,将子项whetever4上移至partner1下,删除operand节点objects/afolder/methods/partner2/operand2:仅含单个子项,将子项whatever3上移至partner2下(与operandx同级),删除operand2节点
转换规则
- jq表达式仅可引用
item、name、description字段,不可指定自定义字段(如beep1、keep1等) - 保留原有字段的顺序
- 上移子项时,需放置在原父节点所在的位置
- 仅当节点下仅有单个子项时才执行上移操作,多子项时不处理
输入示例JSON
{ "item": [ { "name": "objects", "description": "root f", "item": [ { "name": "external", "item": [ { "name": "buuu", "keep1": {} }, { "name": "biii", "keep1": {} } ], "description": "Whatever comment." }, { "name": "afolder", "item": [ { "name": "methods", "item": [ { "name": "blaaa", "keep1": {} }, { "name": "empty", "description": "Whatever2.", "item": [] }, { "name": "partner1", "item": [ { "name": "operand", "item": [ { "name": "whetever4", "beep1": { }, "beep2": [] } ] } ] }, { "name": "empty2", "description": "Whatever3." }, { "name": "partner2", "item": [ { "name": "operandx", "beep2": [] }, { "name": "operand2", "item": [ { "name": "whatever3", "beep1": { }, "beep2": [] } ] } ] } ] } ] } ] } ] }
预期输出示例JSON
{ "item": [ { "name": "objects", "description": "root f", "item": [ { "name": "external", "item": [ { "name": "buuu", "keep1": {} }, { "name": "biii", "keep1": {} } ], "description": "Whatever comment." }, { "name": "afolder", "item": [ { "name": "methods", "item": [ { "name": "blaaa", "keep1": {} }, { "name": "partner1", "item": [ { "name": "whetever4", "beep1": { }, "beep2": [] } ] }, { "name": "partner2", "item": [ { "name": "operandx", "beep2": [] }, { "name": "whatever3", "beep1": { }, "beep2": [] } ] } ] } ] } ] } ] }
jq解决方案
使用递归jq表达式实现转换,逻辑分为清理空节点、合并单子项节点两步:
def clean: # 过滤空节点:保留有额外字段或非空子项的节点 select( (keys - ["name", "description", "item"] | length > 0) or (.item // [] | length > 0) ) | # 递归处理子项,合并符合条件的单节点 (.item // []) |= ( map(clean) | map( # 检查是否可合并:无额外字段且仅含单个子项 if (keys - ["name", "description", "item"] | length == 0) and (.item | length == 1) then .item[0] # 用子项替换当前节点,实现上移 else . end ) ); clean
使用方法
将上述表达式保存为clean-tree.jq,执行以下命令:
jq -f clean-tree.jq input.json > output.json
逻辑说明
- 递归清理空节点:通过
select语句过滤掉仅含基础字段且无有效子项的节点 - 合并单子项节点:遍历子项时,判断节点是否无额外字段且仅有一个子项,若是则用子项替换当前节点,完成层级上移
- 保留字段顺序:jq默认保留原有字段顺序,无需额外处理
内容的提问来源于stack exchange,提问作者Tamás Horváth
相关产品推荐
相关产品推荐

