'Ansible: Error reading JSON: Invalid data passed to 'loop', it requires a list, got this instead

Here is my JSON

[
  {
    "?xml": {
      "attributes": {
        "encoding": "UTF_8",
        "version": "1.0"
      }
    }
  },
  {
    "jdbc_data_source": [
      {
        "attributes": {
          "xmlns": "http://xmlns.oracle.com/weblogic/jdbc_data_source"
        }
      },
      {
        "name": "canwebds"
      },
      {
        "jdbc_driver_params": [
          {
            "url": "jdbc:oracle:thin:@//myhost.mrshmc.com:1521/OLTT206"
          },
          {
            "driver_name": "oracle.jdbc.OracleDriver"
          },
          {
            "properties": {
              "property": [
                {
                  "name": "user"
                },
                {
                  "value": "WEB_USER"
                }
              ]
            }
          },
          {
            "password_encrypted": "{AES}BcqmURyYoCkLvC5MmREXsfpRMO93KPIubqUAbb95+nE="
          }
        ]
      },
      {
        "jdbc_connection_pool_params": [
          {
            "initial_capacity": "1"
          },
          {
            "statement_cache_type": "LRU"
          }
        ]
      },
      {
        "jdbc_data_source_params": {
          "jndi_name": "canwebds"
        }
      }
    ]
  },
  {
    "?xml": {
      "attributes": {
        "encoding": "UTF_8",
        "version": "1.0"
      }
    }
  },
  {
    "jdbc_data_source": [
      {
        "attributes": {
          "xmlns": "http://xmlns.oracle.com/weblogic/jdbc_data_source"
        }
      },
      {
        "name": "dsARSVelocity"
      },
      {
        "jdbc_driver_params": [
          {
            "url": "jdbc:oracle:thin:@myhost:1521:DB01"
          },
          {
            "driver_name": "oracle.jdbc.OracleDriver"
          },
          {
            "properties": {
              "property": [
                {
                  "name": "user"
                },
                {
                  "value": "AP05"
                }
              ]
            }
          },
          {
            "password_encrypted": "{AES}wP5Se+OQdR21hKiC2fDw1WPEaTMU5Sc17Ax0+rmjmPI="
          }
        ]
      },
      {
        "jdbc_connection_pool_params": [
          {
            "initial_capacity": "1"
          },
                    {
            "statement_cache_type": "LRU"
          }
        ]
      },
      {
        "jdbc_data_source_params": [
          {
            "jndi_name": "dsARSVel"
          },
          {
            "global_transactions_protocol": "OnePhaseCommit"
          }
        ]
      }
    ]
  }
]

I need to print the below for any jdbc_data_source found

expected output:

jdbc_data_source name is has username and jndi name <jndi_name>

which will translate as:

jdbc_data_source name is cwds has username CAN_USER and jndi name cwdsjndi

Below is something i tried but it does not work:

  - name: create YML for server name with DB
    debug:
      msg: "{{ dsname.0.name }} has jndi {{ dsurl[0]['jdbc_driver_params'][2]['properties][0]['property'][1]['value'] }}"
    loop: "{{ jsondata[1] }}"
    vars:
      dsname: "{{ item.jdbc_data_source| selectattr('name', 'defined') | list }}"
      dsurl: "{{ item.jdbc_data_source| selectattr('jdbc_driver_params', 'defined') | list }}"

However, it does not get me the desired output. Below is the error i get:

fatal: [localhost]: FAILED! => {"msg": "Invalid data passed to 'loop', it requires a list, got this instead: {'jdbc_data_source': [{'attributes': {'xmlns': 'http://xmlns.oracle.com/weblogic/jdbc_data_source', 'xml

If I loop loop: "{{ jsondata }}", then it works but the desired values still do not get printed.

Kindly suggest.



Solution 1:[1]

this playbook does the job:

- hosts: localhost
  gather_facts: no
  vars:
    json: "{{ lookup('file', 'file.json') | from_json }}"
  tasks:
    - name: display datas
      debug:
        msg: "jdbc_data_source name is {{ name }} has username: {{ user }} and jndi name: {{ jndiname }}"
      loop: "{{ json }}" 
      when: item.jdbc_data_source is defined
      vars:
        datasource1: "{{ item.jdbc_data_source | selectattr('jdbc_driver_params', 'defined') }}"
        properties: "{{ (datasource1.0.jdbc_driver_params | selectattr('properties', 'defined')).0.properties }}"

        name: "{{ (item.jdbc_data_source | selectattr('jdbc_data_source_params', 'defined')).0.jdbc_data_source_params.jndi_name }}"
        user: "{{ (properties.property |  selectattr('value', 'defined')).0.value }}"
        jndiname: "{{ (item.jdbc_data_source | selectattr('name', 'defined') ).0.name}}"

result:

skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}}) 
ok: [localhost] => "msg": "jdbc_data_source name is cwds has username: CAN_USER and jndi name: cwdsjndi"
}
skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}}) 
ok: [localhost] => "msg": "jdbc_data_source name is dsvelcw has username: WEB_USER and jndi name: dsvelcw"

if you have a mixed of list and dictionary, change:

  vars:
    datasource1: "{{ item.jdbc_data_source | selectattr('jdbc_driver_params', 'defined') }}"
    properties: "{{ (datasource1.0.jdbc_driver_params | selectattr('properties', 'defined')).0.properties }}"
    params: "{{ (item.jdbc_data_source | selectattr('jdbc_data_source_params', 'defined')).0.jdbc_data_source_params }}"

    name: "{{ params.jndi_name if params is mapping else (params | selectattr('jndi_name', 'defined')).0.jndi_name }}"           
    user: "{{ (properties.property |  selectattr('value', 'defined')).0.value }}"
    jndiname: "{{ (item.jdbc_data_source | selectattr('name', 'defined') ).0.name}}"

i test if its a dict else its a list..

result with new json:

skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}}) 
    "msg": "jdbc_data_source name is canwebds has username: WEB_USER and jndi name: canwebds"
}
skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}}) 
    "msg": "jdbc_data_source name is dsARSVel has username: AP05 and jndi name: dsARSVelocity"
}

some explanations:

(item.jdbc_data_source | selectattr('jdbc_data_source_params', 'defined')) selectattr create a list with all keys jdbc_data_source_params present in the list jdbc_data_source, here there are only one key jdbc_data_source_params, so the 0.jdbc_data_source_params selects the first key.

then we check if params is a dict else its a list

if you want to understand, i suggest you to decompose in lot of actions and display with debug the result.

FYI selectattr equivalent with json_query

selectattr('something', 'defined') = json_query("[?something]")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1