I'm trying to move one set of nodes to be children of a different element.
SampleFile.xml
<root>
<OuterNode>
<Node>
<Name>NodeA</Name>
</Node>
<Node>
<Name>NodeB</Name>
</Node>
<SpecialNode>
<Name>NodeZ</Name>
</SpecialNode>
</OuterNode>
</root>
Using this code, I can only get the first "Node" to move
import xml.etree.ElementTree as etree
tree = etree.parse('sampleFile.xml')
root = tree.getroot()
next_node = etree.Element('NextOuterNode')
root.append(next_node)
for parent in root:
if parent.tag == 'OuterNode':
for child in parent:
if child.tag == 'Node':
parent.remove(child)
next_node.append(child)
After fumbling around for a while I found that this will move both nodes
outer_node = tree.find('./OuterNode')
for e in tree.findall('./OuterNode/Node'):
next_node.append(e)
outer_node.remove(e)
So my question is what is the difference in the iterable lists that
for child in parent
and
for e in tree.findall
generate?
I can't say for certain (and I'm guessing the only way to definitively answer is by digging through the source), but I would guess that the difference is the first is positional and iterates through the parent and the second makes a new list and iterates through that.
Consider, when you run this:
for child in parent
If you have two elements, and it is iterating through the parent, if you remove the first element, the second becomes the first and since the loop has already done the first element, its not repeated and the looping is complete.
With the second:
for e in tree.findall
iteration through the parent isn't implemented like this (as the elements could be anywhere), so a new list of references is made. Moving these doesn't change the references, so it loops correctly.
This is all conjecture of course, but it fits with what you describe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With