Reputation: 11259
I need "get_ancestors_recursively" function.
A sample run can be
>>> dump(tr)
<anc1>
<anc2>
<element> </element>
</anc2>
</anc1>
>>> input_element = tr.getiterator("element")[0]
>>> get_ancestors_recursively(input_element)
['anc1', 'anc2']
Can somebody help me with this ?
Upvotes: 4
Views: 4322
Reputation: 16573
You can use a tree traversal algorithm. This does the task in linear time.
def xml_get_ancestors(element: Element, root: Element):
ancestors = []
for node in root.iter():
if node == element:
return ancestors
while len(ancestors) > 0 and node not in ancestors[-1]:
ancestors.pop()
ancestors.append(node)
return None
Upvotes: 0
Reputation: 21
Found this little gem from lots of googling (http://elmpowered.skawaii.net/?p=74)
parent = root.findall(".//{0}/..".format(elem.tag))
root here is your root node of the tree. elem is the actual element object you get from iterating.
This does require you to know the root, which may mean changing how you set up for XML parsing, but it's minor at best.
Upvotes: 0
Reputation: 946
Another option is LXML, which provides useful extensions to the built in ElementTree api. If you're willing to install an external module, it has a nice Element.getparent()
function that you could simply call recursively until reaching ElementTree.getroot()
. This will probably be the fastest and most elegant solution (as the lxml.etree module
introduces pointer attributes for the Elements that point to their parents, so instead of searching the entire tree for the proper parent/child
pairs).
Upvotes: 3
Reputation: 7855
In the latest version of ElementTree (v1.3 or later), you can simply do
input_element.find('..')
recursively. However, the ElementTree that ships with Python doesn't have this functionality, and I don't see anything in the Element class that looks upwards.
I believe this means you have to do it the hard way: via an exhaustive search of the element tree.
def get_ancestors_recursively(e, b):
"Finds ancestors of b in the element tree e."
return _get_ancestors_recursively(e.getroot(), b, [])
def _get_ancestors_recursively(s, b, acc):
"Recursive variant. acc is the built-up list of ancestors so far."
if s == b:
return acc
else:
for child in s.getchildren():
newacc = acc[:]
newacc.append(s)
res = _get_ancestors_recursively(child, b, newacc)
if res is not None:
return res
return None
This is slow because of the DFS, and cranks out a lot of lists for garbage collection, but if you can deal with that it should be fine.
Upvotes: 2