JMESPath expression to flatten array of objects, each with nested arrays of objects

折月煮酒 提交于 2020-12-10 09:28:49

问题


I have JSON containing an array of databases, each database has an array of users, e.g.

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

I would like to produce a flat array of databases and users, i.e.

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

In SQL terms this would be a cartesian join or cartesian product, but I'm not sure of the correct term in a tree structure. The closest I've got so far is

databases[].users[]

which produces

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

and

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

which produces

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

Addendum: I'm happy to accept "You can't do that with JMESPath, here's why ..." as an answer. An HN Comment`` hints at this

can't reference parents when doing iteration. Why? All options for iteration, [* ] and map, all use the iterated item as the context for any expression. There's no opportunity to get any other values in


回答1:


An option would be to loop subelements

  tasks:
    - set_fact:
        my_db: "{{ my_db + [ item.0|combine(item.1) ] }}"
      loop: "{{ lookup('subelements',databases,'users') }}"



回答2:


You can't do this with just JMESPath, because JMESPath expressions can only refer to a single scope. There's no way to reach the outer scope (database objects), when the current scope is a user object. JEP 11 would allow access to other scopes, but it hasn't been accepted after several years.

On Ansible it can be done with other filters (h/t Vladimir), and some ugliness

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

Explanation

As a reminder, our starting point is

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

the subelements filter transforms this into a list of Python tuple pairs

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

to_json and from_json convert the tuple pairs to lists (JMESPath for Python ignores tuples)

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

json_query selects the desired db and user values

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


来源:https://stackoverflow.com/questions/54160360/jmespath-expression-to-flatten-array-of-objects-each-with-nested-arrays-of-obje

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!