使用ElementTree删除多个元素的正确方法是什么?

时间:2018-04-05 08:39:45

标签: python xml elementtree

我一直在使用python和ElementTree来操作相当大的xml文件并取得了成功。我发现我很难删除多个元素,特别是当它们是root的子元素时。如果我有4个元素编号1-4,则只使用“for elem in root”子句删除1和3。

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<CrossDEV culture-info="en-US" platform-version="2.40.8" product-version="2.40.8">
<MyStuff.Interface.Common.Objects.ActionItem ImportMode="Default">
    <TargetObjectKey>FOOSTUFF1</TargetObjectKey>
</MyStuff.Interface.Common.Objects.ActionItem>
<MyStuff.Interface.Common.Objects.ActionItem ImportMode="Default">
    <TargetObjectKey>FOOSTUFF2</TargetObjectKey>
</MyStuff.Interface.Common.Objects.ActionItem>
<MyStuff.Interface.Common.Objects.ActionItem ImportMode="Default">
    <TargetObjectKey>FOOSTUFF3</TargetObjectKey>
</MyStuff.Interface.Common.Objects.ActionItem>
<MyStuff.Interface.Common.Objects.ActionItem ImportMode="Default">
    <TargetObjectKey>FOOSTUFF4</TargetObjectKey>
</MyStuff.Interface.Common.Objects.ActionItem>
</CrossDEV>

代码:

def RemoveElementActionItem():
        sTag = 'SourceObjectKey'
        sTag2 = 'TargetObjectKey'
        sPattern = 'CHE-ZUG'
        r=0
        e=0
        global myroot
        if myroot is not None:
                print ('Root:', myroot)
                for elem in myroot:
                        e+=1
                        print ('Elem:',e, elem)
                        aRemove = True
                        bRemove = True
                        o = elem.find(sTag)
                        if o is not None and o.text.find(sPattern,0) > -1:
                                aRemove = False

                        p = elem.find(sTag2)
                        if o is not None and o.text.find(sPattern,0) > -1:
                                bRemove = False

                        if bRemove and aRemove:
                                myroot.remove(elem)
                                r+=1
                                print ('Removed:', myroot, elem)
                        else:
                                print ('   Keep:', myroot, elem, o , p, aRemove, bRemove)
        return r

在上面的代码中,我正在寻找孙子的特定文本值。我拼凑了一个简单的xml文件,每个ActionItem都无法通过测试,因此应该将其删除。相反,只有4个中的2个被删除。

我的猜测是,当删除列表中的第一个时,地址会发生变化,以便跳过第二个地址。接下来,第三个被删除,列表再次向前移动。

因为在这个简单的情况下应该删除所有4个元素,有什么更好的方法来构造我的代码?如果可以的话,我宁愿坚持使用相同的库,因为我已经投入了大量的时间并且尚未探索lxml或其他库。

注意,我一直在玩不同的方法来定位根对象(myroot)。我把它作为一个参数,一个返回值,在这里作为全局。我各方面都有相同的结果。

2 个答案:

答案 0 :(得分:0)

code.py

import sys
from xml.etree import ElementTree as ET


XML_STR = """\
<?xml version="1.0" encoding="utf-8"?>
<RootNode>
  <ChildNode DummyIndex="0">
    <GrandChildNode DummyIndex="0">GrandChildText</GrandChildNode>
    <GrandChildNode_ToRemove DummyIndex="0">GrandChildText</GrandChildNode_ToRemove>
  </ChildNode>
  <ChildNode DummyIndex="1">
    <GrandChildNode_ToDelete DummyIndex="0">GrandChildText</GrandChildNode_ToDelete>
    <GrandChildNode_ToRemove DummyIndex="0">GrandChildText</GrandChildNode_ToRemove>
  </ChildNode>
  <ChildNode DummyIndex="2">
    <GrandChildNode DummyIndex="0">GrandChildText</GrandChildNode>
    <GrandChildNode_ToDelete DummyIndex="0">GrandChildText</GrandChildNode_ToDelete>
    <GrandChildNode_ToRemove DummyIndex="0">GrandChildText</GrandChildNode_ToRemove>
  </ChildNode>
  <ChildNode DummyIndex="3">
    <GrandChildNode_ToRemove DummyIndex="0">GrandChildText</GrandChildNode_ToRemove>
    <GrandChildNode_ToRemove DummyIndex="1">GrandChildText</GrandChildNode_ToRemove>
  </ChildNode>
  <ChildNode DummyIndex="4">
    <GrandChildNode_ToDelete DummyIndex="0">GrandChildText</GrandChildNode_ToDelete>
    <GrandChildNode_ToRemove DummyIndex="0">GrandChildText</GrandChildNode_ToRemove>
    <GrandChildNode_ToDelete DummyIndex="1">GrandChildText</GrandChildNode_ToDelete>
  </ChildNode>
  <ChildNode DummyIndex="5">
    <GrandChildNode_ToDelete DummyIndex="0">GrandChildText</GrandChildNode_ToDelete>
  </ChildNode>
  <ChildNode DummyIndex="6">
    <GrandChildNode DummyIndex="0">GrandChildText</GrandChildNode>
    <GrandChildNode_ToRemove DummyIndex="0">GrandChildText</GrandChildNode_ToRemove>
  </ChildNode>
  <ChildNode DummyIndex="7">
    <GrandChildNode_ToDelete DummyIndex="0">GrandChildText</GrandChildNode_ToDelete>
    <GrandChildNode_ToRemove DummyIndex="0">____OTHERTEXT____</GrandChildNode_ToRemove>
    <GrandChildNode DummyIndex="0">GrandChildText</GrandChildNode>
  </ChildNode>
  <ChildNode DummyIndex="8"/>
</RootNode>
"""

REMOVE_GRANDCHILD_TAGS = ["GrandChildNode_ToDelete", "GrandChildNode_ToRemove"]
REMOVE_GRANDCHILD_TEXT = "Child"


def is_node_subject_to_delete(node):
    removable_child_nodes_count = 0
    for remove_tag in REMOVE_GRANDCHILD_TAGS:
        for child_node in node.findall(remove_tag):
             if REMOVE_GRANDCHILD_TEXT in child_node.text:
                removable_child_nodes_count += 1
                break
    return removable_child_nodes_count == len(REMOVE_GRANDCHILD_TAGS)


def main():
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    #print(XML_STR)
    root_node = ET.fromstring(XML_STR)
    print("Root node has {:d} children\n".format(len(root_node.findall("ChildNode"))))
    to_remove_child_nodes = list()
    for child_node in root_node:
        if is_node_subject_to_delete(child_node):
            to_remove_child_nodes.append(child_node)
    print("Removing nodes:")
    for to_remove_child_node in to_remove_child_nodes:
        print("\n  Tag: {}\n  Text: {}\n  Attrs: {}".format(to_remove_child_node.tag, to_remove_child_node.text.strip(), to_remove_child_node.items()))
        root_node.remove(to_remove_child_node)
    print("\nRoot node has {:d} children\n".format(len(root_node.findall("ChildNode"))))


if __name__ == "__main__":
    main()

备注

  • XML_STR:示例 xml (也可以放在单独的文件中)

    • 根节点(&#34; RootNode &#34;)组成,其中包含一个(> = 0)个子节点(&#34) ; ChildNode &#34):
    • 每个子节点:
      • 命名为&#34; ChildNode &#34;
      • 拥有自己的子节点数(&gt; = 0)(&#34; GrandChildNode * &#34;)
      • 每个子节点(root grand child)节点:
        • 具有以下名称之一(我猜名称结尾不仅仅是自我解释):
          1. &#34; GrandChildNode &#34;
          2. &#34; GrandChildNode_ToDelete &#34;
          3. &#34; GrandChildNode_ToRemove &#34;
        • 有文字(可能是NULL,空白或只是由不可打印的字符组成)
  • REMOVE_GRANDCHILD_TAGS - 标记名称列表,这样,如果(根子项)节点的子项与列表中的所有标记匹配,则可以将其删除 - 替换{ {1}}和sTag - (请查看下面的sTag2备注),如果需要其他标记(例如is_node_subject_to_delete),则可以将其添加到列表(不需要其他副本 / 粘贴操作)

  • GrandChildNode_ToErase - 上一项的2 nd 条件:如果节点文本名称包含该文本(&#34; Child < / em>&#34;) - 如果满足两个条件,则节点 delete 能够
  • REMOVE_GRANDCHILD_TEXT - 检查是否可以删除参数(is_node_subject_to_delete(node) - root child):
    • 规则,如上所述(两者必须为真(&amp;&amp; )):
      1. 如果代码位于黑名单node)中 - 而不是复制代码,则代码为REMOVE_GRANDCHILD_TAGS(最外层)循环
      2. 如果文字包含&#34;注定&#34;文本
  • for - 一般包装函数
    • 如图所示,只有索引 1 2 4 0 )的节点才适合缺失
    • 与用户的接口
    • 我建议迭代根子节点一次黄金法则:&#34; 永远不会使迭代迭代失效&# 34; - 发生在您的情况下),并且对于每个节点,如果 delete 能够,将其保存到列表中(由于 Python 与引用一起工作,它&& #39;并不昂贵),最后,删除该列表中的所有元素(如果有的话)
    • 它确实比&#34; 突破循环&#34;更有效率。建议(特别是在处理巨大的根子节点数时)

<强>输出

main

答案 1 :(得分:0)

虽然这里的其他答案非常有用,但我个人却无法使用它,因为我无法让每个孩子都有相同的名字。相反,我最终遍历Element Tree的方式是使用while循环,在这种情况下,如果必须删除孩子,我会减小end变量(而不是增加计数器)。

通过减少最终目标,可以避免出现“越界”错误

下面是一个为简单起见而遍历字符串的示例:

i = 0
word = 'Python'
end = len(word)

while i < end:
    letter = word[i]
    if letter == 'h':
        word = word.replace(letter, '')
        end-=1
        continue
    print('Current Letter :' + letter)
    i+=1

如果将此应用于元素树,则代码看起来或多或少相同,除了使用replace代替root.remove(child),其中{{1 }}

我希望这可以帮助某人。感谢您的阅读。