如何优化jq中复杂路径下条件更新字段的实现?
JSON复杂路径下的条件更新优化方案咨询
示例输入
{ "very": { "un-fortunate": { "p-a-t-h": { "first": { "veryDeepSubTree": {}, "someValue": 1 }, "second": { "veryDeepSubTree": {}, "someValue": 2 }, "third": { "veryDeepSubTree": {}, "someValue": 3 } } } } }
期望输出
为所有值为奇数的someValue字段加10。
当前实现
jq '.very|=(.["un-fortunate"]|=(.["p-a-t-h"] |=( . as $root| reduce keys[] as $k ({}; . + {($k): ($root|.[$k]|.someValue|=(.+10))}) )))'
该命令嵌套括号过多,可读性与可维护性极差。
疑问前提
- 若
|=的右侧操作数非简单表达式,必须加括号才能编译通过; - 路径过于复杂时,无法一次性表达多层路径;
- 除了难以理解的
foreach,只能通过reduce遍历键来复制并更新对象字段。
问题
- 有没有更简洁的方式从根节点表达这类复杂路径?
- 是否存在比遍历键的
reduce更简便的字段更新逻辑实现方式? - 在shell中编写此类命令时,如何兼顾可读性与可编辑性?
解决方案
1. 简化复杂路径的表达
jq支持点语法+方括号的组合路径写法,无需层层嵌套|=。对于带特殊字符的键,直接用方括号包裹即可,一次性定位到目标子树:
.very["un-fortunate"]["p-a-t-h"]
后续更新操作直接衔接在该路径后,避免多层嵌套的冗余写法。
2. 更简便的字段更新逻辑
完全不需要用reduce遍历键,jq的.[]可以直接遍历对象的所有子值,结合条件判断和|=即可完成精准更新:
保留完整JSON结构的最简实现
jq '.very["un-fortunate"]["p-a-t-h"] |= ( .[] |= (select(.someValue % 2 == 1) | .someValue += 10) )'
逻辑拆解:
.very["un-fortunate"]["p-a-t-h"] |= ...:定位到目标子树,后续操作仅修改该子树内容.[] |= ...:遍历子树下的所有对象(first/second/third)select(.someValue % 2 == 1):筛选出someValue为奇数的对象.someValue += 10:对符合条件的字段执行加10操作
如果只需要输出被修改的子对象,可简化为:
jq '.very["un-fortunate"]["p-a-t-h"][] | select(.someValue % 2 == 1) | .someValue += 10'
3. Shell中兼顾可读性与可编辑性的写法
推荐两种实用写法:
换行加反斜杠拆分命令
把命令按逻辑拆分成多行,每行对应一个步骤,用反斜杠保持命令连续性:
jq '.very["un-fortunate"]["p-a-t-h"] |= ( .[] |= ( select(.someValue % 2 == 1) | .someValue += 10 ) )'
Here-Document传递脚本
通过here-doc传递jq脚本,无需考虑引号转义,可读性拉满:
jq -f - << 'EOF' .very["un-fortunate"]["p-a-t-h"] |= ( .[] |= ( select(.someValue % 2 == 1) | .someValue += 10 ) ) EOF
其中-f -表示从标准输入读取jq脚本,单引号包裹的EOF可避免shell解析脚本内的变量。
内容的提问来源于stack exchange,提问作者Martin Mucha
相关产品推荐
相关产品推荐

