Home > Enterprise >  how to deal with missing keys in JSON array when reading in Ansible
how to deal with missing keys in JSON array when reading in Ansible

Time:03-15

Below is my JSON file:

[
  {
    "?xml": {
      "attributes": {
        "encoding": "UTF-8",
        "version": "1.0"
      }
    }
  },
  {
    "domain": [
      {
        "name": "mydom"
      },
      {
        "domain-version": "12.2.1.3.0"
      },
      {
        "server": [
          {
            "name": "AdminServer"
          },
          {
            "ssl": {
              "name": "AdminServer"
            }
          },
          {
            "listen-port": "12400"
          },
          {
            "listen-address": "mydom.host1.bank.com"
          }
        ]
      },
      {
        "server": [
          {
            "name": "myserv1"
          },
          {
            "ssl": [
              {
                "name": "myserv1"
              },
              {
                "login-timeout-millis": "25000"
              }
            ]
          },
          {
            "log": [
              {
                "name": "myserv1"
              },
              {
                "file-name": "/web/bea_logs/domains/mydom/myserv1/myserv1.log"
              }
            ]
          }
        ]
      },
      {
        "server": [
          {
            "name": "myserv2"
          },
          {
            "ssl": {
              "name": "myserv2"
            }
          },
         {
           "reverse-dns-allowed": "false"
         },
          {
            "log": [
              {
                "name": "myserv2"
              },
              {
                "file-name": "/web/bea_logs/domains/mydom/myserv2/myserv2.log"
              }
            ]
          }
        ]
      }
    ]
  }
]

I need to get log list's name and file-name like below using ansible code.

myserv1_log: "/web/bea_logs/domains/mydom/myserv1/myserv1.log"
myserv2_log: "/web/bea_logs/domains/mydom/myserv2/myserv2.log"

There are two challenges that i m facing.

  1. server may not always be the 3rd key of domain array.
  2. log array may not alway be a key for all server arrays and thus should not be printed. For example. server name AdminServer does not have any log list while myserv1 & myserv2 do have.

I need an ansible code to print the desired for the dynamically changing json.

Note: server will always be a key in the domain array

I'm posting with reference to my similar query here: unable to ideally parse a json file in ansible

Kindly suggest.

CodePudding user response:

you just test if both keys exist:

- hosts: localhost
  gather_facts: no
  vars:
    json: "{{ lookup('file', './file.json') | from_json }}"
  tasks:
    - name: display
      debug:
        msg: "name: {{ servername }} --> filename: {{ filename }}"
      loop: "{{ json[1].domain }}"
      vars:
        servername: "{{ item.server.0.name }}_log"
        filename: "{{ item['server'][2]['log'][1]['file-name'] }}"
      when: item.server is defined and item.server.2.log is defined

result:

TASK [display] 
skipping: [localhost] => (item={'name': 'USWL1212MRSHM01'}) 
skipping: [localhost] => (item={'domain-version': '12.2.1.3.0'}) 
skipping: [localhost] => (item={'server': [{'name': 'AdminServer'}, {'ssl': {'name': 'AdminServer'}}, {'listen-port': '12400'}, {'listen-address': 'myhost1'}]}) 
ok: [localhost] => (item={'server': [{'name': 'myserv1'}, {'ssl': {'name': 'myserv1'}}, {'log': [{'name': 'myserv1'}, {'file-name': '/web/bea_logs/domains/mydom/myserv1/myserv1.log'}]}]}) => {
    "msg": "name: myserv1_log --> filename: /web/bea_logs/domains/mydom/myserv1/myserv1.log"
}
ok: [localhost] => (item={'server': [{'name': 'myserv2'}, {'ssl': {'name': 'myserv2'}}, {'log': [{'name': 'myserv2'}, {'file-name': '/web/bea_logs/domains/mydom/myserv2/myserv2.log'}]}]}) => {
    "msg": "name: myserv2_log --> filename: /web/bea_logs/domains/mydom/myserv2/myserv2.log"
}

As you can see, when the condition is not true, the action is skipped...

you could simplify by testing only key log, because in your case, keylog is always linked to key server

when: item.server.2.log is defined   

CodePudding user response:

Given the data

mydata:
  - ?xml:
      attributes:
        encoding: UTF-8
        version: '1.0'
  - domain:
    - name: USWL1212MRSHM01
    - domain-version: 12.2.1.3.0
    - server:
      - name: AdminServer
      - ssl:
          name: AdminServer
      - listen-port: '12400'
      - listen-address: myhost1
    - server:
      - name: myserv1
      - ssl:
          name: myserv1
      - log:
        - name: myserv1
        - file-name: /web/bea_logs/domains/mydom/myserv1/myserv1.log
    - server:
      - name: myserv2
      - ssl:
          name: myserv2
      - log:
        - name: myserv2
        - file-name: /web/bea_logs/domains/mydom/myserv2/myserv2.log

Use json_query to select the attributes log

_logs: "{{ mydata|json_query(_query) }}"
_query: '[].domain[][].server[].log'

gives

_logs:
  - - name: myserv1
    - file-name: /web/bea_logs/domains/mydom/myserv1/myserv1.log
  - - name: myserv2
    - file-name: /web/bea_logs/domains/mydom/myserv2/myserv2.log

Next, combine the items and use items2dict to create the dictionary

logs_dict: "{{ _logs|map('combine')|list|
                     items2dict(key_name='name', value_name='file-name') }}"

gives the expected result

logs_dict:
  myserv1: /web/bea_logs/domains/mydom/myserv1/myserv1.log
  myserv2: /web/bea_logs/domains/mydom/myserv2/myserv2.log

Put the variables as appropriate, e.g.

- hosts: localhost
  vars:
    mydata: "{{ lookup('file', 'test-658-data.yml')|from_yaml }}"
    _logs: "{{ mydata|json_query(_query) }}"
    _query: '[].domain[][].server[].log'
    logs_dict: "{{ _logs|map('combine')|list|
                         items2dict(key_name='name', value_name='file-name') }}"
  tasks:
    - debug:
        var: logs_dict

Q: The name is not the name of the log but the name of the server

A: Generally, select keys and values, and combine the dictionary. The expressions below give the same result

mydata: "{{ lookup('file', 'test-658-data.yml')|from_yaml }}"
_logs: "{{ mydata|json_query(_query) }}"
_query: '[].domain[][].server'
_arr: "{{ _logs|map('combine')|
                selectattr('name', 'defined')|
                selectattr('log', 'defined') }}"
_names: "{{ _arr|map(attribute='name')|list }}"
_files: "{{ _arr|map(attribute='log.1.file-name')|list }}"
logs_dict: "{{ dict(_names|zip(_files)) }}"
  • Related