Home > Software design >  How to skip null values in json query?
How to skip null values in json query?

Time:04-22

Im trying to pull values from a json file but I do not want values that are null? How can I easily skip null values?

I am just trying to find the easiest and simplest solution to parsing JSON into YALM and not have formatting errors in YAML. Maybe I am over complicating it.

JSON:

{
    "configuration": {
       "protocols" : {
            "bgp" : {
                "group" : [
                {
                    "name" : "IBGP",
                    "neighbor" : [
                    {
                        "name" : "192.168.1.2",
                        "description" : "router-2"
                    }
                    ]
                },
                {
                    "name" : "EBGP",
                    "neighbor" : [
                    {
                        "name" : "192.168.2.2",
                        "description" : "router-3",
                        "local-address" : "192.168.2.1",
                        "peer-as" : "65555"
                    }
                    ]
                }
            ]
            }
        }
    }
}

Play:

- name: parse bgp
  set_fact:
    bgp: "{{ read | to_json | from_json | json_query(bgp_var) }}"
  vars:
    bgp_var: 'configuration.protocols.bgp.group[].{group_name:name,neighbors:[{peer_ip:neighbor[].name | [0], peer_description:neighbor[].description | [0], peer_as:neighbor[]."peer-as" | [0] }]}'

- copy:
    content: >-
      routing_protocols:
        bgp:
          {{ bgp | to_nice_yaml(indent=2, width=1337, sort_keys=False) | indent(4) }}  
    dest: "{{ inventory_hostname }}_routing_protocols.yaml"

Returns:

routing_protocols:
  bgp:
    - group_name: IBGP
      neighbors:
      - peer_as: null # < Is there a way to skip this anytime a value is null?
        peer_description: router-2
        peer_ip: 192.168.1.2

    - group_name: EBGP
      neighbors:
      - peer_as: '65555'
        peer_description: router-3
        peer_ip: 192.168.2.2

CodePudding user response:

you could use this playbook with the parameter omit:

  tasks:
    - name: set var
      set_fact:
        bgp: >-
          {{ bgp | d([])   
            [ {'group_name': _gpname, 
               'neighbors': [{'peer_description': _desc, 'peer_ip': _peerip, 'peer_as': _peeras}]
              }
            ] }}      
      loop: "{{ configuration.protocols.bgp.group }}"
      vars:
        _gpname: "{{ item.name }}"
        _desc: "{{ item.neighbor.0.description }}"
        _peeras: "{{ item.neighbor[0]['peer-as'] | d(omit)}}"
        _peerip: "{{ item.neighbor.0.name }}"
    - name: displ
      debug:
        msg: "{{ bgp }}"

result:

ok: [localhost] => {
    "msg": [
        {
            "group_name": "IBGP",
            "neighbors": [
                {
                    "peer_description": "router-2",
                    "peer_ip": "192.168.1.2"
                }
            ]
        },
        {
            "group_name": "EBGP",
            "neighbors": [
                {
                    "peer_as": "65555",
                    "peer_description": "router-3",
                    "peer_ip": "192.168.2.2"
                }
            ]
        }
    ]
}

CodePudding user response:

You can make it in one go in JMESPath, but that would require a double query on the property neighbour, as JMESPath cannot omit a property as Ansible can (for that, see @Frenchy's answer).

So, you would have to have list of list, into which, the first sub list would consist of the neighbour that would contain the key peer-as, and the second sub list, the one that would not contain it. Then you will have to flatten this list of list.

So, here is the copy task:

- copy:  
  content: >-
      {{
        read
        | to_json
        | from_json
        | json_query(query)
        | to_nice_yaml(indent=2)
      }}
    dest: "{{ inventory_hostname }}_routing_protocols.yaml"
  vars:
    query: >-
      {
        routing_protocols: {
          bgp: configuration.protocols.bgp.group[].{
            group_name: name,
            neighbors: [
              neighbor[?"peer-as"].{
                peer_description: description,
                peer_ip: name,
                peer_as: "peer-as"
              },
              neighbor[?"peer-as" == null].{
                peer_description: description,
                peer_ip: name
              }
            ]|[]
          }
        }
      }

After this task, you will end with a YAML file containing your expected:

routing_protocols:
  bgp:
  - group_name: IBGP
    neighbors:
    - peer_description: router-2
      peer_ip: 192.168.1.2
  - group_name: EBGP
    neighbors:
    - peer_as: '65555'
      peer_description: router-3
      peer_ip: 192.168.2.2
  • Related