首页 > 解决方案 > 用于展平对象数组的 JMESPath 表达式,每个对象都有嵌套的对象数组

问题描述

我有一个包含数据库数组的 JSON,每个数据库都有一个用户数组,例如

{"databases": [
  {"db": "db_a", "users": [{"name": "alice"}, {"name": "alex"}]},
  {"db": "db_b", "users": [{"name": "bob"}, {"name": "brienne"}]}
]}

我想生成一个平面的数据库和用户数组,即

[
  {"db": "db_a", "name": "alice"},
  {"db": "db_a", "name": "alex"},
  {"db": "db_b", "name": "bob"},
  {"db": "db_b", "name": "brienne"}
]

在 SQL 术语中,这将是笛卡尔连接或笛卡尔积,但我不确定树结构中的正确术语。到目前为止我最接近的是

databases[].users[]

产生

[{"name": "alice"}, {"name": "alex"}, {"name": "bob"}, {"name": "brienne"}]

databases[].{db: db, name: users[].name}

产生

[
  {"db": "db_a", "name": ["alice", "alex"]},
  {"db": "db_b", "name": ["bob", "brienne"]}
]

附录:我很高兴接受“你不能用 JMESPath 做到这一点,这就是为什么......”作为答案。HN 评论“暗示了这一点

进行迭代时无法引用父母。为什么?迭代的所有选项 [* ] 和 map 都使用迭代项作为任何表达式的上下文。没有机会获得任何其他价值

标签: jsonansiblejmespath

解决方案


您不能使用 JMESPath 执行此操作,因为 JMESPath 表达式只能引用单个范围。当当前范围是用户对象时,无法到达外部范围(数据库对象)。JEP 11将允许访问其他范围,但几年后仍未被接受。

在 Ansible 上,它可以用其他过滤器(h/t Vladimir)完成,还有一些丑陋

databases_users: "{{ 
    databases | subelements('users')
              | to_json | from_json
              | json_query('[*].{db: [0].db, name: [1].name}')
}}"

解释

提醒一下,我们的出发点是

[ {"db": "db_a", "users": [{"name": "alice"}, {"name": "alex"}]},
  ...]

过滤器将其subelements转换为 Python 元组对列表

[ ({"db": "db_a", "users": [{"name": "alice"}, {"name": "alex"}]},
   {"name": "alice"}),
  ...]

to_json并将from_json元组对转换为列表(Python 的 JMESPath 忽略元组)

[ [{"db": "db_a", "users": [{"name": "alice"}, {"name": "alex"}]},
   {"name": "alice"}],
  ...]

json_query选择所需dbuser

[ {"db": "db_a", "name": "alice"},
  ...]

推荐阅读