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

2024-04-20 01:13:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在使用python和ElementTree来操作相当大的xml文件,成功率参差不齐。我发现我很难移除多个元素,尤其是当它们是根的子元素时。如果我有4个编号为1-4的元素,那么只有1和3将使用“forelem in root”子句删除。在

<?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>

代码:

^{2}$

在上面的代码中,我在孙辈中搜索特定的文本值。我拼凑了一个简单的xml文件,每个ActionItem测试失败,因此应该删除它。取而代之的是,4个人中只有2个被移除。在

我的猜测是,当第一个从列表中删除时,地址会发生变化,从而跳过第二个地址。下一步,删除第三个,列表再次向前移动。在

既然在这个简单的例子中,所有4个元素都应该被删除,那么构建代码的更好方法是什么?如果可以的话,我更喜欢使用同一个库,因为我已经在它上面投入了很多时间,而且还没有探索lxml或其他库。在

注意,我一直在使用不同的方法来确定根对象(myroot)的范围。我把它作为一个参数,一个返回值,这里作为一个全局变量。我每种方法都有相同的结果。在


Tags: 文件方法代码default元素objectsversionxml
1条回答
网友
1楼 · 发布于 2024-04-20 01:13:18

代码.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(也可以放在单独的文件中)

    • 由一个根节点(“RootNode”)组成,该根节点的子节点数(“子节点”):
    • 每个子节点:
      • 命名为“ChildNode
      • 具有自己的子节点数(>;=0)(sundrenode*
      • 每个子节点(根子节点):
        • 具有以下名称之一(我想名称结尾不仅仅是不言而喻的):
          1. 子节点
          2. 子节点要删除
          3. 移动子节点”
        • 有一个文本(可能是NULL,为空,或者只是由不可打印的字符组成)
  • REMOVE_GRANDCHILD_TAGS-标记名的列表,以便如果(根子节点)节点的子节点与列表中的所有标记匹配,则可以将其删除-替换sTag和{}-(请检查下面的is_node_subject_to_delete注释),如果需要另一个标记(例如GrandChildNode_ToErase),则可以将其删除,只需将其添加到列表中即可(无需其他复制/粘贴操作)

  • REMOVE_GRANDCHILD_TEXT-前一项的2nd条件:如果节点文本名称包含该文本(“Child”)-如果两个条件都满足,则节点可以删除
  • is_node_subject_to_delete(node)-检查是否可以删除参数(node-根子级):
    • 规则,如上所述(两个必须为真(&;):
      1. 如果标记在黑名单中,而不是重复代码,它是一个for(最外层)循环
      2. 如果文本包含“注定”文本
  • main-一个通用包装函数
    • 如图所示,只有索引124(基于0的)的节点才适合删除
    • 与用户的接口
    • 我建议在根子节点上迭代一次(黄金法则:“永远不会使iterable iterating on失效——这在您的例子中发生过),并且对于每个节点,如果delete可以,则将其保存到一个列表中(由于Python可以处理引用,因此成本不高),最后,删除列表中的所有元素(如果有的话)
    • 它绝对比“跳出循环”建议的效率更高(尤其是在处理巨大的根子节点数时)

输出

(py35x64_test) e:\Work\Dev\StackOverflow\q049667831>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32

Root node has 9 children

Removing nodes:

  Tag: ChildNode
  Text:
  Attrs: [('DummyIndex', '1')]

  Tag: ChildNode
  Text:
  Attrs: [('DummyIndex', '2')]

 Tag: ChildNode
  Text:
  Attrs: [('DummyIndex', '4')]

Root node has 6 children

相关问题 更多 >