在Python中替换整个XML树

2024-10-02 12:31:08 发布

您现在位置: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>

我需要的是将elem3替换为NewElem,这将导致:

<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


Tags: 文件heremorerootxmlfoldersomethingstuff
3条回答

下面使用的是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 )

可以使用子元素添加新元素

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>

注意NewElemelem4之后,不是在elem3的确切位置,而是在folder之内,所以我认为这解决了我想要的问题

相关问题 更多 >

    热门问题