使用jq的reduce函数转换数组为对象时保留重复key的解决方案
问题与解决方案
问题描述
我有一个经jq处理大JSON文件后得到的数组,格式如下:
[ { "Action": { "name1": { "product": { "summary": "Summary Product" } } } }, { "Action": { "name2": { "gproduct": { "summary": "Summary Product" } } } }, { "Action": { "name3": { "product1": { "summary": "Summary Product1" } } } }, { "Action": { "name3": { "product2": { "summary": "Summary Product2" } } } } ]
我期望将其转换为包含重复name3条目的对象:
{ "Action": { "name1": { "product": { "summary": "Summary Product" } }, "name2": { "gproduct": { "summary": "Summary Product" } }, "name3": { "product1": { "summary": "Summary Product1" } }, "name3": { "product2": { "summary": "Summary Product2" } } } }
但使用jq命令:
reduce .[] as $o ({}; reduce ($o|keys)[] as $key (.; .[$key] += $o[$key] ))
得到的结果中name3仅保留了最后一个product:
{ "Action": { "name1": { "product": { "summary": "Summary Product" } }, "name2": { "gproduct": { "summary": "Summary Product" } }, "name3": { "product2": { "summary": "Summary Product2" } } } }
请问如何修改jq命令,才能让同名的nameX对应的所有product都被单独保留,而不被覆盖?
解决方案
首先需要明确:JSON规范不允许对象存在重复键,绝大多数JSON解析器遇到重复键时,只会保留最后一个键值对,所以你期望的格式在标准JSON中是不合法的,无法被正常解析。
标准合法的处理方式(推荐)
将同名的nameX对应的结构存入数组,调整后的目标结构如下:
{ "Action": { "name1": [ { "product": { "summary": "Summary Product" } } ], "name2": [ { "gproduct": { "summary": "Summary Product" } } ], "name3": [ { "product1": { "summary": "Summary Product1" } }, { "product2": { "summary": "Summary Product2" } } ] } }
对应的jq命令:
{ Action: reduce .[].Action as $a ({}; reduce ($a | keys[]) as $k (.; .[$k] += [$a[$k]]) ) }
命令逻辑说明:
- 遍历原始数组的每个元素,提取其中的
Action对象 - 对每个
Action内的键(如name1、name3),将对应的值以数组元素的形式追加到结果的对应键中 - 最终同名的
name会对应一个数组,包含所有相关的product结构
非标准的重复键输出(不推荐)
如果因特殊需求必须输出带有重复键的非标准JSON(兼容性极差,多数工具无法正确解析),可以用jq的字符串拼接方式实现:
jq -r ' "{\"Action\": {" + (.[] | to_entries[] | .value | to_entries[] | "\"\(.key)\": \(tojson)") | join(", ") + "}}"'
内容的提问来源于stack exchange,提问作者Maria Petkova
相关产品推荐
相关产品推荐

