我正在编写一个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
集,并在修改元素时基本上重建一棵树,但如果它看起来像是一个非优化的解决方案,因为我需要始终检查祖先之间的重复项并进行迭代经常穿过树 - 但也许我也错过了那里的一些东西。
这是一个解决方案。您基本上需要创建一个新的空树,您将添加您想要保留的所有项目,而不是那些不符合条件的项目
# 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)
解决此问题的一种方法是创建一个新的 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)
发布的答案不起作用。如果将来有人遇到类似的问题,我已经通过使用 2 个循环的解决方法解决了这个问题:
第一个循环创建项目集的“负”,即我想要删除的项目集,它首先定义为空
deleteset = set()
第二个循环迭代创建的集合并删除先前定义的元素
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()
在删除包含子元素的元素后不进入不再存在的树枝。