Search code examples
xmlansiblexml-parsing

Unable to parse query a value node corresponding to a name node from a XML using Ansible


I have a XML from which I need to parse a value using Ansible.
Below is the XML:

<?xml version="1.0" encoding="utf-8"?>
<Activity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://example.com/2005/06/StandardHeader/" xmlns="http://example.com/xsd/frm/v1/Activity/ManageInternalCustomerChangeRequest">
  <ActivityDetails>
    <activityId>basdsds5-21aa-4ec4-b453-5f08af0fc612</activityId>
    <activityType>ServiceIdRecordUpdate</activityType>
    <activityStartDate>%DateTime%</activityStartDate>
    <listOfActivityAttribute>
      <activityAttribute>
        <name>CustomerId</name>
        <value>%CustomerId%</value>
      </activityAttribute>
      <activityAttribute>
        <name>AssetInstanceId</name>
        <value>%AssetInstanceId%</value>
      </activityAttribute>
      <activityAttribute>
        <name>ServiceId</name>
        <value>%ServiceId%</value>
      </activityAttribute>
      <activityAttribute>
        <name>SupplierServiceId</name>
        <value>%SupplierServiceId%</value>
      </activityAttribute>
    </listOfActivityAttribute>
  </ActivityDetails>
</Activity>

I am able to parse and extract the value of the node activityId i.e basdsds5-21aa-4ec4-b453-5f08af0fc612 using this Ansible task

- name: Step-3:Read an element's attribute values
  xml:      
    path: /tmp/test.xml
    xpath: /x:Activity/x:ActivityDetails/x:activityId
    content: text
    namespaces:
      x: "http://example.com/xsd/frm/v1/Activity/ManageInternalCustomerChangeRequest"
  register: xmlresp

Which gives me the value

{"ansible_facts": {"Value": "basdsds5-21aa-4ec4-b453-5f08af0fc612"}, "changed": false}

However I am unable to parse or extract the value for CustomerId using the below. I have tried prefixing the namespace across the XPath, without any more success.

- name: Step-4:Read Customer ID from XML
  xml:
    path: /tmp/test.xml
    xpath: /x:Activity/ActivityDetails/listOfActivityAttribute/activityAttribute[name='CustomerId']/value
    content: text
    namespaces:
      x: "http://example.com/xsd/frm/v1/Activity/ManageInternalCustomerChangeRequest"
  register: xmlresp1

It fails with the error

FAILED! => {"changed": false, "msg": "Xpath /x:Activity/x:ActivityDetails/x:listOfActivityAttribute/x:activityAttribute[name='CustomerId'] does not reference a node!"}

I need to extract the value %CustomerId%, using Ansible.


Solution

  • You really have to add the namespace on everything, which include the name node: x:name!

    So, your XPath end up being something like:

    xpath: >-
      /x:Activity
      /x:ActivityDetails
      /x:listOfActivityAttribute
      /x:activityAttribute[x:name='CustomerId']
      /x:value
    

    Given the two tasks:

    - xml:
        path: test.xml
        xpath: >-
          /x:Activity
          /x:ActivityDetails
          /x:listOfActivityAttribute
          /x:activityAttribute[x:name='CustomerId']
          /x:value
        content: text
        namespaces:
          x: "{{ _x }}"
      register: xmlresp
      vars:
        _x: http://example.com/xsd/frm/v1/Activity/ManageInternalCustomerChangeRequest
    
    - debug:
        var: xmlresp.matches.0['{' ~ _x ~  '}value']
      vars:
        _x: http://example.com/xsd/frm/v1/Activity/ManageInternalCustomerChangeRequest
    

    The debug ends up giving:

    ok: [localhost] => 
      xmlresp.matches.0['{' ~ _x ~  '}value']: '%CustomerId%'
    

    Side note: to DRY this code, you can define the _x variable representing the namespace on an upper level, e.g. at the playbook or inventory level.