如果我想要保留一组给定的元素,如何从 lxml 树中删除项目?

问题描述 投票:0回答:3

我正在编写一个Python xml (netconf)解析器,目标是从服务器获取rpc-reply xml,修改一些项目,并生成一个最小的配置.xml,然后可以将其发送到服务器。

修改 GUI 中的值时,我将修改的元素添加到集合中,以及它们的祖先元素兄弟元素(不包含子元素),因为这将是“最小可行”结果文件的内容

我正在处理的示例(缩短的)xml:

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:a1cfef75-dba4-4fdf-81eb-8d5f65d35511">
  <data>
    <bridges xmlns="urn:ieee:std:802.1Q:yang:ieee802-dot1q-bridge">
      <bridge>
        (...)
      </bridge>
    </bridges>
    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
      <interface>
        <name>PORT_0</name>
        <description>random</description>
        <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
        <bridge-port xmlns="urn:ieee:std:802.1Q:yang:ieee802-dot1q-bridge">
            (...)
        </bridge-port>
      </interface>
      <interface>
        (...)
      </interface>
    </interfaces>
    <keystore xmlns="urn:ietf:params:xml:ns:yang:ietf-keystore">
        (...)
    </keystore>
  </data>
</rpc-reply>

我在同时使用

.iter()
.remove()
时发现了一个问题,即当我想修改例如,它仅删除 分支,然后
.iter()
循环不会返回到 ,或返回到它的直接祖先,很可能是因为我已经删除了有关祖先的所有信息。换句话说,
.iter().
循环在第一次遇到“最后一个叶子”元素时停止。

我使用以下代码来删除项目,

self.itemstokeep
是一组要保留的
etree.Element

for item in treecopy.iter():
    if not item in self.itemstokeep:
        if not item.getparent() == None:
            item.getparent().remove(item)
    else:
        continue

你能推荐任何好方法来解决这个问题或完全解决这个问题吗?
到目前为止,与我在这里找到的答案的最大区别是,我不知道要删除哪些项目,只知道要保留哪些项目,而且除了 2 个顶级元素之外,我不会总是具有相同的输入结构,这使得通常的“xpath”方法变得复杂......

我也考虑过不再创建一个

itemstokeep
集,并在修改元素时基本上重建一棵树,但如果它看起来像是一个非优化的解决方案,因为我需要始终检查祖先之间的重复项并进行迭代经常穿过树 - 但也许我也错过了那里的一些东西。

python lxml netconf
3个回答
0
投票

这是一个解决方案。您基本上需要创建一个新的空树,您将添加您想要保留的所有项目,而不是那些不符合条件的项目

# Parse the XML
root = ET.fromstring(xml_data)

# Set of elements to keep (you have this already)
items_to_keep = set()

# A function to recursively copy elements you want to keep
def copy_elements(element):
    if element in items_to_keep:
        # Clone the element and its attributes
        new_element = ET.Element(element.tag, element.attrib)
        # Copy the text (if any)
        new_element.text = element.text
        new_element.tail = element.tail
        # Recursively copy child elements
        for child in element:
            new_child = copy_elements(child)
            new_element.append(new_child)
        return new_element
    else:
        # If not in the items to keep, return None
        return None

# Create a new XML tree, starting from the root
new_root = copy_elements(root)

# Create a new XML tree and add the new root
new_tree = ET.ElementTree(new_root)

# Serialize the new tree to XML
new_xml = ET.tostring(new_root, encoding='unicode')

print(new_xml)

0
投票

解决此问题的一种方法是创建一个新的 XML 树,从根元素开始,然后仅添加要保留的元素。这样,您就可以避免从现有树中删除元素的问题,并确保生成的树仅包含所需的元素。具体方法如下:

from xml.etree import ElementTree as ET

# Assuming your original XML is stored in the 'xml_string' variable
root = ET.fromstring(xml_string)

# Create a new XML tree with the root element
new_tree = ET.Element(root.tag, nsmap=root.nsmap)

# A set of elements to keep
elements_to_keep = {"interfaces", "interface", "name", "description"}

# Initialize a stack for ancestors
ancestors = [new_tree]

# Iterate through the original tree
for elem in root.iter():
    if elem.tag in elements_to_keep:
        # Add the element to the current ancestor
        current_ancestor = ancestors[-1]
        current_ancestor.append(elem)

        # If the element has children, push it onto the stack of ancestors
        if list(elem):
            ancestors.append(elem)
    elif elem.tag == ancestors[-1].tag:
        # If we encounter an element with the same tag as the current ancestor,
        # pop it from the stack to move back up the tree
        ancestors.pop()

# Convert the new tree to a string
new_xml_string = ET.tostring(new_tree).decode()

# Print the resulting XML
print(new_xml_string)

0
投票

发布的答案不起作用。如果将来有人遇到类似的问题,我已经通过使用 2 个循环的解决方法解决了这个问题:

  1. 第一个循环创建项目集的“负”,即我想要删除的项目集,它首先定义为空

    deleteset = set()

  2. 第二个循环迭代创建的集合并删除先前定义的元素

        for item in treecopy.iter():
           if not item in self.copyitems_to_keep:                 
              if not item.getparent() == None:                     
                 deleteset.add(item)
              else:                     
                 continue
    
        for item in deleteset:
           item.getparent().remove(item)
    

感谢 Hermann12 对原始问题的评论,我还意识到我在代码的另一部分中的错误 - 最初我没有使用

deepcopy()
来创建
treecopy
根元素,这导致了应用程序中的另一类问题。

万一将来有人偶然发现这个线程,我仍然很想知道是否有办法强制

.iter()
在删除包含子元素的元素后不进入不再存在的树枝。

© www.soinside.com 2019 - 2024. All rights reserved.