Search code examples
ansiblejinja2

How to extract multiple nested attributes from all items in a list of dicts?


I have a list of objects and I need to pull out multiple attributes from the items in the list so that the result is a list of only those attributes and none of the others. Here's what my original data looks like:

[
  {
    "failed": true,
    "msg": "Error while parsing document: files/file1.xml (Opening and ending tag mismatch)",
    "invocation": {
      "module_args": {
        "path": "files/file1.xml"
      }
    }
  },
  {
    "failed": true,
    "msg": "Error while parsing document: files/file2.xml (Opening and ending tag mismatch)",
    "invocation": {
      "module_args": {
        "path": "files/file2.xml"
      }
    }
  }
]

I need to map the msg and invocation.module_args.path attributes into new objects. The resulting list should look like this:

[
  {
    "msg": "Error while parsing document: files/file1.xml (Opening and ending tag mismatch)",
    "path": "files/file1.xml"
  },
  {
    "msg": "Error while parsing document: files/file2.xml (Opening and ending tag mismatch)",
    "path": "files/file2.xml"
  }
]

If I can possibly define new attribute names that would be preferred, but it's lower priority:

[
  {
    "error": "Error while parsing document: files/file1.xml (Opening and ending tag mismatch)",
    "file": "files/file1.xml"
  },
  {
    "error": "Error while parsing document: files/file2.xml (Opening and ending tag mismatch)",
    "file": "files/file2.xml"
  }
]

I can do this by creating an additional task file and looping over it, creating each dict object and updating the list but I'd rather just use a single task and/or Jinja2 filter if possible to simplify things.

Is this possible?


Solution

  • Given the mre data

        l1:
          - failed: true
            invocation:
              module_args: {path: files/file1.xml}
            msg: 'Error: files/file1.xml'
          - failed: true
            invocation:
              module_args: {path: files/file2.xml}
            msg: 'Error: files/file2.xml'
    

    Use json_query. For example,

      err: "{{ l1|json_query('[].{error: msg,
                                  file: invocation.module_args.path}') }}"
    

    gives what you want

      err:
      - error: 'Error: files/file1.xml'
        file: files/file1.xml
      - error: 'Error: files/file2.xml'
        file: files/file2.xml
    

    Example of a complete playbook for testing

    - hosts: all
    
      vars:
    
        l1:
          - failed: true
            invocation:
              module_args: {path: files/file1.xml}
            msg: 'Error: files/file1.xml'
          - failed: true
            invocation:
              module_args: {path: files/file2.xml}
            msg: 'Error: files/file2.xml'
    
        err: "{{ l1|json_query('[].{error: msg,
                                    file: invocation.module_args.path}') }}"
      tasks:
    
        - debug:
            var: err