替换 Python 中的整个 XML 树
Replacing whole XML tree in Python
如何替换整个 XML 树?
假设我有一个如下所示的文件:
<root>
<folder>
<elem1>'something here'</elem1>
<elem2>'more stuff here'</elem2>
<elem3>
<sub1>'something else here'</sub1>
<sub2>'blablabla'</sub2>
</elem3>
<elem4>'even more stuff here with subelements too'</elem4>
</folder>
</root>
我还有另一个 xml 文件,可以替代 elem3
,看起来像:
<NewElem>
<Difsub1>'something else here, but different'</Difsub1>
<Difsub2>'all sorts of different blablabla'</Difsub2>
</NewElem>
我需要用 NewElem
替换 elem3
,结果是:
<root>
<folder>
<elem1>'something here'</elem1>
<elem2>'more stuff here'</elem2>
<NewElem>
<Difsub1>'something else here, but different'</Difsub1>
<Difsub2>'all sorts of different blablabla'</Difsub2>
</NewElem>
<elem4>'even more stuff here with subelements too'</elem4>
</folder>
</root>
我正在使用 xml.etree.ElementTree
并尝试 append
但我最终得到 NewElem
,在 </folder>
之后。我无法删除更新的 xml 文件的 elem3
。
我试着用这个附加它:
import xml.etree.ElementTree as ET
tree = ET.parse('base.xml')
baseroot = tree.getroot()
tree2 = ET.parse('new.xml')
newroot = tree2.getroot()
old_element = baseroot.findall('.//elem3')
baseroot.append(newroot)
baseroot.remove(old_element)
因此 NewElem
被附加到文件夹之后,我需要在 elem3
所在的同一位置,或者至少在 <folder>
内
另外,删除时出现错误:
TypeError: remove() argument must be xml.etree.ElementTree.Element, not list
如果我改成
old_element = baseroot.find('.//elem3')
baseroot.append(newroot)
baseroot.remove(old_element)
我得到非常相似的错误:ValueError: list.remove(x): x not in list
下面使用的是 lxml
包,比 ElementTree
更高级的 XML 包。
您可以使用父元素的 index(element)
方法找到要替换的节点的索引。
一旦你得到它,你可以使用来自父节点的 insert(index, element)
并将新节点插入旧节点的位置。
接下来是通过remove(element)
方法删除旧节点
例子'p'是父元素,a
是要替换的p
的子节点,b
是新的子节点:
p.insert(p.index(a), b) # insert b before a
p.remove(a)
使用ElementTree
,您需要先找到旧元素的索引:
p = et.Element('parent') # parent node
a = et.Element('child1') # this is the child node to be replaced
b = et.Element('child2') # this is the new child node
p.append(a)
index = list(p).index(a)
p[index] = b
如果你坚持使用'ElementTree',首先你应该意识到擦除只对直接包含你要删除的节点的节点有效。
所以在这样搜索的时候
old_element = baseroot.find('.//elem3')
很好,你不能从 baseroot 中删除它,你需要获取它所在的位置,然后从那里删除它。最简单的一定是得到它的父
old_element_parent = xml.find('.//elem3/..')
old_element_parent.remove(old_element )
可以使用 SubElement 添加新元素
a = ET.SubElement(old_element_parent, 'NewElement')
首先感谢@egur和@Henrik的指导,特意指点不同的库。我正在使用 lxml (and this documentation),所以我可以使用以下代码做我想做的事:
from lxml import etree
tree = etree.parse('base.xml')
new = etree.tostring(etree.parse('new.xml')) # parsing to a string so it can be appended later
for elem in tree.xpath(".//elem3"): # finds the parent where the element to be replaced are
elem.getparent().append(etree.fromstring(new)) #append in the end of parent, the fromstring() is because append don't like elementTree
elem.getparent().remove(elem)
print(etree.tostring(tree, encoding="utf-8").decode('utf-8'))
以上代码产生以下结果:
<root>
<folder>
<elem1>'something here'</elem1>
<elem2>'more stuff here'</elem2>
<elem4>'even more stuff here with subelements too'</elem4>
<NewElem>
<Difsub1>'something else here, but different'</Difsub1>
<Difsub2>'all sorts of different blablabla'</Difsub2>
</NewElem></folder>
</root>
请注意,NewElem
在 elem4
之后,不完全是 elem3
所在的位置,而是在 folder
之内,所以我认为这解决了我想要的问题。
如何替换整个 XML 树? 假设我有一个如下所示的文件:
<root>
<folder>
<elem1>'something here'</elem1>
<elem2>'more stuff here'</elem2>
<elem3>
<sub1>'something else here'</sub1>
<sub2>'blablabla'</sub2>
</elem3>
<elem4>'even more stuff here with subelements too'</elem4>
</folder>
</root>
我还有另一个 xml 文件,可以替代 elem3
,看起来像:
<NewElem>
<Difsub1>'something else here, but different'</Difsub1>
<Difsub2>'all sorts of different blablabla'</Difsub2>
</NewElem>
我需要用 NewElem
替换 elem3
,结果是:
<root>
<folder>
<elem1>'something here'</elem1>
<elem2>'more stuff here'</elem2>
<NewElem>
<Difsub1>'something else here, but different'</Difsub1>
<Difsub2>'all sorts of different blablabla'</Difsub2>
</NewElem>
<elem4>'even more stuff here with subelements too'</elem4>
</folder>
</root>
我正在使用 xml.etree.ElementTree
并尝试 append
但我最终得到 NewElem
,在 </folder>
之后。我无法删除更新的 xml 文件的 elem3
。
我试着用这个附加它:
import xml.etree.ElementTree as ET
tree = ET.parse('base.xml')
baseroot = tree.getroot()
tree2 = ET.parse('new.xml')
newroot = tree2.getroot()
old_element = baseroot.findall('.//elem3')
baseroot.append(newroot)
baseroot.remove(old_element)
因此 NewElem
被附加到文件夹之后,我需要在 elem3
所在的同一位置,或者至少在 <folder>
内
另外,删除时出现错误:
TypeError: remove() argument must be xml.etree.ElementTree.Element, not list
如果我改成
old_element = baseroot.find('.//elem3')
baseroot.append(newroot)
baseroot.remove(old_element)
我得到非常相似的错误:ValueError: list.remove(x): x not in list
下面使用的是 lxml
包,比 ElementTree
更高级的 XML 包。
您可以使用父元素的 index(element)
方法找到要替换的节点的索引。
一旦你得到它,你可以使用来自父节点的 insert(index, element)
并将新节点插入旧节点的位置。
接下来是通过remove(element)
方法删除旧节点
例子'p'是父元素,a
是要替换的p
的子节点,b
是新的子节点:
p.insert(p.index(a), b) # insert b before a
p.remove(a)
使用ElementTree
,您需要先找到旧元素的索引:
p = et.Element('parent') # parent node
a = et.Element('child1') # this is the child node to be replaced
b = et.Element('child2') # this is the new child node
p.append(a)
index = list(p).index(a)
p[index] = b
如果你坚持使用'ElementTree',首先你应该意识到擦除只对直接包含你要删除的节点的节点有效。
所以在这样搜索的时候
old_element = baseroot.find('.//elem3')
很好,你不能从 baseroot 中删除它,你需要获取它所在的位置,然后从那里删除它。最简单的一定是得到它的父
old_element_parent = xml.find('.//elem3/..')
old_element_parent.remove(old_element )
可以使用 SubElement 添加新元素
a = ET.SubElement(old_element_parent, 'NewElement')
首先感谢@egur和@Henrik的指导,特意指点不同的库。我正在使用 lxml (and this documentation),所以我可以使用以下代码做我想做的事:
from lxml import etree
tree = etree.parse('base.xml')
new = etree.tostring(etree.parse('new.xml')) # parsing to a string so it can be appended later
for elem in tree.xpath(".//elem3"): # finds the parent where the element to be replaced are
elem.getparent().append(etree.fromstring(new)) #append in the end of parent, the fromstring() is because append don't like elementTree
elem.getparent().remove(elem)
print(etree.tostring(tree, encoding="utf-8").decode('utf-8'))
以上代码产生以下结果:
<root>
<folder>
<elem1>'something here'</elem1>
<elem2>'more stuff here'</elem2>
<elem4>'even more stuff here with subelements too'</elem4>
<NewElem>
<Difsub1>'something else here, but different'</Difsub1>
<Difsub2>'all sorts of different blablabla'</Difsub2>
</NewElem></folder>
</root>
请注意,NewElem
在 elem4
之后,不完全是 elem3
所在的位置,而是在 folder
之内,所以我认为这解决了我想要的问题。