从解析的XML树中删除元素会中断迭代

前端之家收集整理的这篇文章主要介绍了从解析的XML树中删除元素会中断迭代前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我想解析一个xml文件,然后通过删除所选元素来处理结果树.我的问题是删除一个元素会破坏迭代元素的循环.

考虑以下xml数据:

<results>
    <group>
        <a />
        <b />
        <c />
    </group>
</results>

代码

import xml.etree.ElementTree as ET

def showGroup(group,s):
    print(s + '  len=' + str(len(group)))
    print('<group>' )
    for e in group:
        print('   <' + e.tag + '>')
    print('</group>\n')

def processGroup(group):
    for e in group:
        if e.tag != 'a':
            group.remove(e)
            showGroup(group,'removed <' + e.tag + '>')

tree = ET.parse('x.xml')
root = tree.getroot()

for group in root:
    processGroup(group)

我期望for循环处理元素< a>,< b>和< c>为了.尤其是:

>处理< a>不应该删除任何元素
>处理< b>应删除< b>
>处理< c>应该删除< c>

我希望生成的树在< group>内有一个元素. (< a>元素),len(组)将返回1.

相反,在处理< b>之后,for循环决定已经满足结束测试,并且它不处理元素< c>.如果是,则< c>将被删除.相反,我留下了一个元素< a>的树.和< c>,len(组)返回2.

删除所选元素时,我需要做什么来处理所有三个元素? PS:欢迎任何关于风格的评论或更好的做事方式.

更新:如果在删除元素后没有代码,那么丑陋的黑客会以某种效率为代价“修复”问题.但在我的真实程序中,修剪循环后会有很多代码.

for e in group:
    if e.tag != 'a':
        group.remove(e)
        showGroup(group,'removed <' + e.tag + '>')
        processGroup(group)

我假设如果for循环中断,那么在开始时再次启动组可能会解决问题.递归是一种整洁的方式 – 以重新处理已经检查但未被删除的所有元素为代价.

我对这个解决方案不满意.

问题是你正在从正在迭代的东西中删除元素,当你删除一个元素时,剩下的元素会被移位,所以你最终可能会删除不正确的元素:

一个简单的解决方案是遍历树的副本或使用反向:

复制:

def processGroup(group):
    # creates a shallow copy so we are removing from the original
    # but iterating over a copy. 
    for e in group[:]:
        if e.tag != 'a':
            group.remove(e)
            showGroup(group,'removed <' + e.tag + '>')

逆转:

def processGroup(group):
    # starts at the end,as the container shrinks.
    # when an element is removed,we still see
    # elements at the same position when we started out loop.
    for e in reversed(group):
        if e.tag != 'a':
            group.remove(e)
            showGroup(group,'removed <' + e.tag + '>')

使用复制逻辑:

In [7]: tree = ET.parse('test.xml')

In [8]: root = tree.getroot()

In [9]: for group in root:
   ...:         processGroup(group)
   ...:     
removed <b>  len=2
<group>
   <a>
   <c>
</group>

removed <c>  len=1
<group>
   <a>
</group>

您也可以使用ET.tostring代替for循环:

import xml.etree.ElementTree as ET

def show_group(group,s):
    print(s + '  len=' + str(len(group)))
    print(ET.tostring(group))


def process_group(group):
    for e in group[:]:
        if e.tag != 'a':
            group.remove(e)
            show_group(group,'removed <' + e.tag + '>')

tree = ET.parse('test.xml')
root = tree.getroot()

for group in root.findall(".//group"):
    process_group(group)

猜你在找的XML相关文章