-1

This is an XML parser code snippet which returns with None value. This is a large XML file that has a lot of subfields like this:

<root>
    <field name ="1">
        <field name ="2" showname ="ZZZ">
            <field name ="3" showname="YYY">
                <field name ="4" showname="XXX"/>
            </field>
        </field>
    </field>

The findall() finds all the elements with a tag which are direct children of the current element. I tried this, but it returned with None. It prints nothing also.

def findXXX(field):
    if field.get('name') == 'XXX' :
        return field.get('showname')
    else:
        for fieldchild in field.findall('field'):
            return findXXX(fieldchild)

If I write like this, it prints the correct value, however it returns with None.

def findXXX(field):
    if field.get('name') == 'XXX' :
        print field.get('showname')
        return field.get('showname')
    else:
        for fieldchild in field.findall('field'):
            findXXX(fieldchild)
doniyor
  • 36,596
  • 57
  • 175
  • 260
Vadmeggy
  • 123
  • 7
  • In your first code block, I suspect having an unconditional `return` inside a `for` loop is a logic error; it will only iterate once before terminating. – Kevin Aug 05 '14 at 12:40
  • Your recursion only explores the leftmost branch of the tree because you `return` immediately in your `for` loop. Either accumulate in a list and return at the end of your loop or rewrite it into a generator. – roippi Aug 05 '14 at 12:42
  • BTW, do you mean to test for the `'name'` attribute or the `'showname'` attribute? You are testing against `'XXX'`. – Martijn Pieters Aug 05 '14 at 12:48
  • Does this answer your question? [Why does my recursive function return None?](https://stackoverflow.com/questions/17778372/why-does-my-recursive-function-return-none) – VLAZ Nov 03 '22 at 17:10

2 Answers2

0

In your second version, you are ignoring the return value of the recursive call. Your first version at least still returned that return value:

for fieldchild in field.findall('field'):
    return findXXX(fieldchild)

The return statement in your function returns to the direct caller, not to the caller of the outermost first invocation of findXXX().

However now only the result for the first field in the field.findall() list will return something. You probably want to return the first that is not None:

for fieldchild in field.findall('field'):
    result = findXXX(fieldchild)
    if result is not None:
        return result

This kicks of a recursive search for each field element, and returns the result of the first such recursive calls that is not None.

Note that you don't have to recurse; you can also use an XPath expression to search through the tree without having to recurse yourself:

field.find(".//field[@showname='XXX']")

This returns the first field tag whose name attribute is set to XXX, searching recursively.

Demo:

>>> from xml.etree import ElementTree as ET
>>> tree = ET.fromstring('''\
... <root>
...     <field name ="1">
...         <field name ="2" showname ="ZZZ">
...             <field name ="3" showname="YYY">
...                 <field name ="4" showname="XXX"/>
...             </field>
...         </field>
...     </field>
... </root>
... ''')
>>> tree.find(".//field[@showname='XXX']")
<Element 'field' at 0x108519bd0>
>>> tree.find(".//field[@showname='XXX']").get('name')
'4'
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

May I suggest that you use BeautifulSoup. It is easy to learn, has good documentation. http://www.crummy.com/software/BeautifulSoup/bs4/doc/

A soup object can be instantiated using the code below, where "markup" is a string containing your XML, or input file handle to the XML file:

BeautifulSoup(markup, "xml")
coder.in.me
  • 1,048
  • 9
  • 19