Search code examples
pythonjsondictionaryjmespath

jmespath: how to search a dict of dict structure


There's an example of search in the tutorial going:

{
  "machines": [
    {"name": "a", "state": "running"},
    {"name": "b", "state": "stopped"},
    {"name": "b", "state": "running"}
  ]
}
In [68]: jmespath.search("machines[?state=='running'].name",p)
Out[68]: ['a', 'b']

However, my structure uses a dictionary rather than a list, e.g.:

In [64]: q={
    ...:   "machines": {
    ...:     "m1":     {"name": "a", "state": "running"},
    ...:     "m2":     {"name": "b", "state": "stopped"},
    ...:     "m3":     {"name": "c", "state": "running"}
    ...:     }
    ...:     }

My different attempts to parse this have failed:

In [65]: jmespath.search("machines[?state=='running'].name",q)
# no output
In [66]: jmespath.search("machines.*[?state=='running'].name",q)
Out[66]: []

In [67]: jmespath.search("machines[*][?state=='running'].name",q)
# no output

How can I perform this search?


Solution

  • You can use a * wildcard expression to select all values from a hash:

    >>> jmespath.search("machines.*", q)
    [{'name': 'a', 'state': 'running'}, {'name': 'b', 'state': 'stopped'}, {'name': 'c', 'state': 'running'}]
    

    Now you have the same structure as before, so you can add [?state=='running'].name to it. Do put the above expression into parentheses, you want it to apply to the array output of the wildcard, not to each individual value in the machines mapping:

    (machines.*)[?state=='running'].name
    

    or use a pipe expression:

    machines.* | [?state=='running'].name
    

    Both give you the desired output:

    >>> jmespath.search("(machines.*)[?state=='running'].name", q)
    ['a', 'c']
    >>> jmespath.search("machines.* | [?state=='running'].name", q)
    ['a', 'c']