如何用jq将对象列表转换为嵌套单对象(Ansible场景)
问题:Ansible中高效转换大型JSON为嵌套字典
在Ansible处理大型JSON响应时,因原结构导致搜索速度慢,需要将字典列表转换为单个嵌套字典。Ansible自带的jmespath无法实现值转键,尝试了支持jq的社区插件,但只能硬编码顶层字典。
示例输入JSON
{ "json": { "@code": "19", "@status": "success", "result": { "@count": "7003", "@total-count": "7003", "entry": [ { "@location": "shared", "@name": "bgp_routers", "static": { "member": [ "core-1", "core-2", "core-3", "supercore" ] } }, { "@location": "shared", "@name": "web_servers", "description": "req-5", "static": { "member": [ "web001", "web002", "web004" ] } }, { "@location": "remote-site-1", "@name": "web_servers-2", "description": "req-100", "static": { "member": [ "web001", "web003", "web004" ] } } ] } } }
期望转换后的嵌套结构
{ "shared": { "bgp_routers": { "@location": "shared", "@name": "bgp_routers", "static": { "member": [ "core-1", "core-2", "core-3", "supercore" ] } }, "web_servers": { "@location": "shared", "@name": "web_servers", "description": "req-5", "static": { "member": [ "web001", "web002", "web004" ] } } }, "remote-site-1": { "web_servers-2": { "@location": "remote-site-1", "@name": "web_servers-2", "description": "req-100", "static": { "member": [ "web001", "web003", "web004" ] } } } }
当前尝试的问题
使用以下jq语句:
.json.result.entry | map({ (.["@location"]): {( .["@name"] ): . }})
得到的结果无法合并相同的shared键,使用add只会保留最后一个shared条目:
[ { "shared": { "bgp_routers": { "@location": "shared", "@name": "bgp_routers", "static": { "member": [ "core-1", "core-2", "core-3", "supercore" ] } } } }, { "shared": { "web_servers": { "@location": "shared", "@name": "web_servers", "description": "req-5", "static": { "member": [ "web001", "web002", "web004" ] } } } }, { "remote-site-1": { "web_servers-2": { "@location": "remote-site-1", "@name": "web_servers-2", "description": "req-100", "static": { "member": [ "web001", "web003", "web004" ] } } } } ]
另外,不使用Ansible的|combine过滤器是因为处理大循环时性能极低,每次迭代都会删除并重建字典。
解决方案:使用jq的reduce和递归合并操作符
要实现相同@location下的条目合并,需用jq的reduce遍历条目,结合递归合并操作符*来合并同位置的字典,替代简单的add。
最终jq语句
.json.result.entry | reduce .[] as $item ({}; . * { ($item["@location"]): { ($item["@name"]): $item } })
逻辑解释
reduce .[] as $item ({}; ...):以空字典为初始值,遍历每个条目$item. * { ... }:使用*操作符递归合并当前结果和新生成的条目字典,相同@location下的子字典会被合并而非覆盖{ ($item["@location"]): { ($item["@name"]): $item } }:为每个条目生成对应嵌套结构,和之前的map逻辑一致,但通过reduce+*实现合并
效果验证
执行上述语句后,会直接输出期望的嵌套结构,且处理大型数据集时性能远优于Ansible的combine循环,因为jq的合并操作是原生高效实现的。
内容的提问来源于stack exchange,提问作者MostProductiveAfter6
相关产品推荐
相关产品推荐

