How can I work with multi level dictionnary?

Viewed 73

I have the following json data to work with:

"result": {
  "json": {
    "licences": {
      "4216": {
        "license": "4512-5421-5134-7413"
      }
    }
  }
}

I first tried to get my expected value with the following debug task:

- name: Display Licence ID
  ansible.builtin.debug:
    var: result.json.licences[0].license

But it returns :

"VARIABLE IS NOT DEFINED!"

As a work-around I'm currently using :

- name: Display Licence ID
  ansible.builtin.debug:
    var: result.json.licences[result.json.licences|first].license

This works but isn't there a better way than repeating the whole variable name?

2 Answers

Your input data is effectively a dict but you are trying to use it as a list. There is no element named 0 (i.e. having a key which value is 0) in your dict called licences (and since it is not a list, there is no first element at index 0 either).

What I understand from your question is that your licences dict will always contain a single key and that you want to get the licence number inside that key whatever the key name is. Please edit your question to be more specific if my understanding is wrong.

One way to do this is:

- name: Display licence ID
  debug:
    msg: "{{ result.json.licences | dict2items | map(attribute='value.license') | first }}"

Explanation:

  • the dict2items filter transforms your result.json.licenses dict
    {
      "4216": {
        "license": "4512-5421-5134-7413"
      }
    }
    
    into a list of key/value dicts
    [
      {
        "key": "4216",
        "value": {
          "license": "4512-5421-5134-7413"
        }
      }
    ]
    
  • the map(attribute='value.license)` filter extracts the nested attribute from each element into a list
    [
      "4512-5421-5134-7413"
    ]
    
  • the first filter keeps only the first element from that list:
    "4512-5421-5134-7413"
    

If you ever have several results and want to get a list of license number, just remove the first filter at the end. You can also build up on this example to get the key for the license if you need it one day.

  • Create the complete path. Attribute '4216' is a string. It must be quoted. Put it into the brackets
    - debug:
        var: result.json.licences['4216'].license
      vars:
        result:
          json:
            licences:
              '4216':
                license: 4512-5421-5134-7413

gives

result.json.licences['4216'].license: 4512-5421-5134-7413
  • If you don't know the number, or, in general, the name of the attribute, use json_query. For example,
    - debug:
        var: result.json.licences|json_query('*.license')|first
  • As a side note: If the attribute were a number 4216 you could have used it without quotes and brackets. For example,
    - debug:
        var: result.json.licences.4216.license
      vars:
        result:
          json:
            licences:
              4216:
                license: 4512-5421-5134-7413

gives

result.json.licences.4216.license: 4512-5421-5134-7413
Related