# Parsing XML where attribute has period in it

**URL:** <https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911>\
**Category:** Get Help\
**Tags:** playbook, xml\
**Created:** [August 17, 2024, 1:53pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911 "2024-08-17T13:53:27Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![jrglynn2](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@jrglynn2](https://forum.ansible.com/u/jrglynn2)\
**Post date:** [August 17, 2024, 1:53pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/1 "2024-08-17T13:53:27Z")

</div>

Hi all…I am trying to parse an XML file. The piece I need to grab is slightly different between versions of the XML.

Old Version that works:  
XML:

```auto
<dataSource 
        <properties serverName="RDS-NAME"/>
</dataSource>

```

Ansible Code (after grabbing XML using Xpath):

```auto
ansible.builtin.set_fact:
  server_name: "{{ db_engine.matches[0].properties['serverName'] }}"

```

So that properly returns `RDS-NAME`  
\*

New Version that I can’t get to work:  
XML:

```auto
<dataSource 
        <properties.oracle serverName="RDS-NAME"/>
</dataSource>

```

Ansible Code (after grabbing XML using Xpath):

```auto
ansible.builtin.set_fact:
  server_name: "{{ db_engine.matches[0].properties.oracle['serverName'] }}"

```

I get an error saying that it `has no attribute 'properties'` … so I think that because there is now a period in the XML (`properties.oracle`), my Ansible code is seeing `properties` and `oracle` as separate.

I have tried various escape characters (`/`, `//`, `\`, `\\`) before the period, and enclosing in quotes (`"{{ db_engine.matches[0].'properties.oracle'['serverName'] }}"`, yet I cannot get it to read it properly.

Any help is greatly appreciated!!! Thanks!

---

<div class="post-metadata">

**Author:** ![anon81884396](https://avatars.discourse-cdn.com/v4/letter/a/b782af/32.png) [@anon81884396](https://forum.ansible.com/u/anon81884396)\
**Post date:** [August 17, 2024, 2:23pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/2 "2024-08-17T14:23:38Z")

</div>

This worked for me:

```auto
    - name: Get XML data
      community.general.xml:
        path: stuff.xml
        xpath: '/dataSource/properties.oracle'
        content: attribute
      register: db_engine

    - name: Show serverName
      ansible.builtin.debug:
        msg: "{{ db_engine.matches[0]['properties.oracle']['serverName'] }}"

```

---

<div class="post-metadata">

**Author:** ![jrglynn2](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@jrglynn2](https://forum.ansible.com/u/jrglynn2)\
**Post date:** [August 17, 2024, 3:44pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/3 "2024-08-17T15:44:57Z")

</div>

@anon81884396 This didn’t work for me …

I should have specified that I have my Xpath set as `xpath: /server/dataSource/*` so I can grab various other pieces.

So let me be more specific:

XML:

```auto
<server 
    <dataSource 
            <jdbcDriver id="oracle" libraryRef="Lib" />
            <properties.oracle serverName="RDS-NAME"/>
    </dataSource>
</server>

```

When I have this for Ansible Code, using my Xpath an your suggested format:

```auto
- name: Get database engine information for Tririga
  community.general.xml:
    path: "XML-FILE"
    xpath: /server/dataSource/*
    content: attribute
  register: db_engine

- name: Echo variables
  ansible.builtin.debug:
    msg:
      - "{{ db_engine.matches[0]['properties.oracle']['serverName'] }}"

```

I now get the error `'dict object' has no attribute 'properties.oracle'`

Thanks!

---

<div class="post-metadata">

**Author:** ![anon81884396](https://avatars.discourse-cdn.com/v4/letter/a/b782af/32.png) [@anon81884396](https://forum.ansible.com/u/anon81884396)\
**Post date:** [August 17, 2024, 6:58pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/4 "2024-08-17T18:58:09Z")

</div>

I believe you’re receiving that error because there are multiple entries under the dataSource element in your XML, so you can’t rely on list index 0 to always include “properties.oracle”. Given the example you have above, db\_engine.matches[0] probably only contains jdbcDriver, and properties.oracle is in db\_engine.matches[1] (depending on if you’re still cutting out content that’s in the XML file). I’m basing this off my local copy of what you have here (I added another XML element called testItem for fun), which results in this:

```auto
TASK [show] *******************************************************************************************************************************************************************************************

ok: [localhost] => {

    "msg": "db_engine.matches[0] is {'jdbcDriver': {'id': 'oracle', 'libraryRef': 'Lib'}} db_engine.matches[1] is {'testItem': {'id': 'example'}} db_engine.matches[2] is {'properties.oracle': {'serverName': 'RDS-NAME'}}"

}

```

You’ll need to loop through the “db\_engine.matches” list for the information you’re after, in some way. Sorry I can’t be more specific as I don’t know enough about your code to recommend what to do exactly, but hopefully this is helpful.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [August 17, 2024, 7:21pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/5 "2024-08-17T19:21:17Z")

</div>

First, fix your XML. You are missing a closing “`>`” after both “`<server`” and “`<dataSource`”.

Once you fix that, you’ll find the error message is actually correct. The dict object “`db_engine.matches[0]`” indeed does not have a “`properties.oracle`” attribute. But dict object “`db_engine.matches[1]`” does.

You probably want a general expression that will work with multiples, like in this example:

```yml
---
# jrglynn2_01.yml
- name: XML parsing
  hosts: localhost
  gather_facts: false
  vars:
    xmlfilename: /tmp/jrglynn2_01.xml
  tasks:
    - name: Create XML file
      ansible.builtin.copy:
        content: |
          <server>
              <dataSource>
                      <jdbcDriver id="oracle" libraryRef="Lib" />
                      <properties.oracle serverName="RDS-NAME-One"/>
                      <properties.oracle serverName="RDS-NAME-Two"/>
                      <something from="nowhere" without="meaning" />
              </dataSource>
          </server>
        dest: '{{ xmlfilename }}'

    - name: Get database engine information for Tririga
      community.general.xml:
        path: '{{ xmlfilename }}'
        xpath: /server/dataSource/*
        content: attribute
      register: db_engine

    - name: Echo db_engine
      ansible.builtin.debug:
        msg: '{{ db_engine }}'

    - name: Echo serverName attribute(s) from properties.oracle(s)
      ansible.builtin.debug:
        msg: "{{ db_engine.matches
                 | map('dict2items')
                 | map('selectattr','key','eq','properties.oracle')
                 | flatten(1)
                 | map(attribute='value.serverName')
              }}"

```

Hope that helps. Good luck.

---

<div class="post-metadata">

**Author:** ![jrglynn2](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@jrglynn2](https://forum.ansible.com/u/jrglynn2)\
**Post date:** [August 17, 2024, 7:52pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/6 "2024-08-17T19:52:40Z")

</div>

> [@anon81884396](#):
>
> matches[1]

> [@utoddl](#):
>
> But dict object “`db_engine.matches[1]`” does.

That was it - looking at [1] worked. I didn’t realize that when I changed the Xpath, it changed the number references for matches.

Thanks all!

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [August 17, 2024, 8:16pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/7 "2024-08-17T20:16:53Z")

</div>

Be careful hard-coding that index. I don’t think the ordering is guaranteed, in which case an expression like

```yaml
             "{{ db_engine.matches
                 | map('dict2items')
                 | map('selectattr','key','eq','properties.oracle')
                 | flatten(1)
                 | map(attribute='value.serverName')
                 | first
              }}"

```

will give you the `serverName` regardless of the order, but `{{ db_engine.matches[1]['properties.oracle']['serverName'] }}` will give you an error if the order ever changes.

---

<div class="post-metadata">

**Author:** ![jhoney12](https://avatars.discourse-cdn.com/v4/letter/j/96bed5/32.png) [@jhoney12](https://forum.ansible.com/u/jhoney12)\
**Post date:** [August 19, 2024, 11:43am UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/8 "2024-08-19T11:43:53Z")

</div>

It looks like the issue you’re facing stems from how Ansible handles attribute names with periods. In your new XML version, properties.oracle is interpreted as a nested dictionary, so you need to adjust your Ansible code accordingly. Try accessing the attribute like this:

ansible.builtin.set\_fact:  
server\_name: “{{ db\_engine.matches[0].properties[‘oracle’][‘serverName’] }}”

Ensure that you correctly navigate through the nested dictionary to fetch the serverName attribute. If the issue persists, double-check that properties.oracle exists within your XML data and isn’t being overlooked.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [August 19, 2024, 12:48pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/9 "2024-08-19T12:48:39Z")

</div>

That’s not what’s going on, though. “`properties.oracle`” really is a key, with the dot in it. If you dump out the registered variable, you get:

```yml
    actions:
      namespaces: {}
      state: present
      xpath: /server/dataSource/*
    changed: false
    count: 2
    failed: false
    matches:
    - jdbcDriver:
        id: oracle
        libraryRef: Lib
    - properties.oracle:
        serverName: RDS-NAME
    msg: 2

```

---

<div class="post-metadata">

**Author:** ![jrglynn2](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@jrglynn2](https://forum.ansible.com/u/jrglynn2)\
**Post date:** [August 20, 2024, 12:32pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/10 "2024-08-20T12:32:53Z")

</div>

> [@utoddl](#):
>
> Be careful hard-coding that index. I don’t think the ordering is guaranteed, in which case an expression like
> 
> ```auto
> "{{ db_engine.matches
> | map('dict2items')
> | map('selectattr','key','eq','properties.oracle')
> | flatten(1)
> | map(attribute='value.serverName')
> | first
> }}"
> 
> ```
> 
> will give you the `serverName` regardless of the order, but `{{ db_engine.matches[1]['properties.oracle']['serverName'] }}` will give you an error if the order ever changes.

Oh that’s interesting. The ordering seems to be consistent as of now, but this does seem like a better way to do it. I will test it out. Thanks @utoddl !!

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [August 20, 2024, 6:24pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/11 "2024-08-20T18:24:54Z")

</div>

_My problem_ with expressions like that (and I wrote that one!) is that as soon as I quit staring at it, I can’t explain _how_ it works. A week later, I can’t remember _why_ it works. A month later I won’t be sure _if_ it works.

In order to remind myself what it’s doing, I have to do a series of `ansible.builtin.debug` tasks, the first one with just the first line or two in it, then adding single lines in subsequent debug tasks so I can see the changes in the data due to each line.

Perhaps it’s just me, but powerful as these expressions can be, they can be equally as opaque. I have some - in production no less - that have “`| map('map',[…])`” in them, and they’d better be documented in the extreme. Don’t edit them without a buddy, and don’t commit/push changes to them unless both of you are convinced it’s solving more problems than it’s creating. And not on a Friday.

---

<div class="post-metadata">

**Author:** ![jrglynn2](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@jrglynn2](https://forum.ansible.com/u/jrglynn2)\
**Post date:** [August 20, 2024, 7:15pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/12 "2024-08-20T19:15:17Z")

</div>

Yeah I am with you @utoddl . I try to make a LOT of comments in the code itself, plus I keep a “code examples/best practices” document for more detail. Right now I am pretty much the only one doing automation, but supposedly more on the team want to get into it …

I haven’t had a chance to play with this yet, hopefully tomorrow - do you know offhand if I can use wildcards? So instead of `map('selectattr','key','eq','properties.oracle')`, use `map('selectattr','key','eq','properties*')`?

Then I can use one expression for either version shown above … where the file might have `properties` or `properties.oracle`

If you don’t know that is fine, I can experiment myself.

Thanks again!

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [August 20, 2024, 7:24pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/13 "2024-08-20T19:24:19Z")

</div>

Not with “`eq`”, but you can use [other tests](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tests.html#testing-strings), like “`match`” and “`search`”:

```yaml
    - name: Echo serverName attribute(s) from properties.oracle(s)
      ansible.builtin.debug:
        msg: "{{ db_engine.matches
                 | map('dict2items')
                 | map('selectattr','key','eq','properties.oracle')
                 | flatten(1)
                 | map(attribute='value.serverName')
              }}"

    - name: Echo serverName attribute(s) from properties.*
      ansible.builtin.debug:
        msg: "{{ db_engine.matches
                 | map('dict2items')
                 | map('selectattr','key','match','properties.*')
                 | flatten(1)
                 | map(attribute='value.serverName')
              }}"

    - name: Echo serverName attribute(s) from .*orac.*
      ansible.builtin.debug:
        msg: "{{ db_engine.matches
                 | map('dict2items')
                 | map('selectattr','key','search','orac')
                 | flatten(1)
                 | map(attribute='value.serverName')
              }}"

```

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [September 19, 2024, 7:24pm UTC](https://forum.ansible.com/t/parsing-xml-where-attribute-has-period-in-it/7911/14 "2024-09-19T19:24:34Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
