ElementTree namespace dictionary not working with find() or findall()

Viewed 211

I'm stumped with how to do the ElementTree namespace dictionary and subsequent find() and findall() calls using the documented sytnax:

A better way to search the namespaced XML example is to create a dictionary with your own prefixes and use those in the search functions:

ns = {'real_person': 'http://people.example.com',
  'role': 'http://characters.example.com'}

for actor in root.findall('real_person:actor', ns):
    name = actor.find('real_person:name', ns)
    print(name.text)
    for char in actor.findall('role:character', ns):
        print(' |-->', char.text)

The issue i'm having is if i try to use the syntax noted in that doc, by passing the "ns" dictionary as a 2nd argument in find() or findall(), i get an empty list. If I type out the full namespace without passing the 2nd argument, it returns all of the expected elements.

I've defined my namespace dictionary as such:

ns = {'ws':'{urn:com.workday/workersync}'}

And here is the ElementTree and root setup:

xmlparser = ET.parse(xmlfile)
xmlroot = xmlparser.getroot()

Here is what i get when i try to use the dictionary shortcut syntax noted in the docs:

>>> xmlroot.findall('ws:Worker', ns)
[]

Just an empty list... Here is what i get if type out the namespace in the call:

xmlroot.findall('{urn:com.workday/workersync}Worker')
[<Element '{urn:com.workday/workersync}Worker' at 0x03220A78>, <Element'{urn:com.workday/workersync}Worker' at 0x0322D8C0>]

That returns the expected 2 elements in my sample file.

Here is what the top of my sample file looks like for reference:

<?xml version="1.0" encoding="UTF-8"?>
<ws:Worker_Sync xmlns:ws="urn:com.workday/workersync" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ws:Header>
        <ws:Version>34.0</ws:Version>
        <ws:Prior_Entry_Time>2020-07-04T21:40:25.822-07:00</ws:Prior_Entry_Time>
        <ws:Current_Entry_Time>2020-07-04T22:03:47.458-07:00</ws:Current_Entry_Time>
        <ws:Prior_Effective_Time>2020-07-04T00:00:00.000-07:00</ws:Prior_Effective_Time>
        <ws:Current_Effective_Time>2020-07-05T00:00:00.000-07:00</ws:Current_Effective_Time>
        <ws:Full_File>true</ws:Full_File>
        <ws:Document_Retention_Policy>30</ws:Document_Retention_Policy>
        <ws:Worker_Count>2</ws:Worker_Count>
    </ws:Header>
    <ws:Worker>
         *<snipped rest of XML data>*

The snipped XML data contains 2 <ws:Worker> elements with many subchildren under them.

I've been messing with this for longer than i'd care to admit. I feel like I'm missing something incredibly obvious, as to my eyes, my code looks like every example i've found online and the example code on the docs.

Please help!

1 Answers

Remove the curly brackets from the URI string. The namespace dictionary should look like this:

ns = {'ws': 'urn:com.workday/workersync'}

Another option is to use a wildcard for the namespace. This is supported for find() and findall() since Python 3.8:

print(xmlroot.findall('{*}Worker'))

Output:

[<Element '{urn:com.workday/workersync}Worker' at 0x033E6AC8>]
Related