使用 elementTree 编辑和复制 xml 块

Editing and duplicating xml block with elementTree

我想按如下方式编辑下面的 XML: 'duplicateAndAddOne' 块应该被复制(名称更改为 'newElements')并且其中的所有项目都增加 1。理想情况下,其中的元素不必单独阅读,而应该成批阅读,因为会有很多项目。

<?xml version="1.0"?>
<data>
    <Strategy name="duplicateAndAddOne">
        <datapoint1>7</datapoint1>
        <datapoint2>9</datapoint2>
    </Strategy>
    <Strategy name="leaveMeAlone">
        <datapoint1>22</datapoint1>
        <datapoint2>23</datapoint2>
    </Strategy>
</data>

这似乎取决于您使用的是 built-in ElementTree 还是 lxml。

使用 lxml,您应该可以使用 copy:

from lxml import etree
e = etree.Element('root')
etree.SubElement(e, 'child1')
etree.SubElement(e, 'child2')

from copy import copy
f = copy(e)
f[0].tag = 'newchild1'
etree.dump(e)
<root>
  <child1/>
  <child2/>
</root>

etree.dump(f)
<root>
  <newchild1/>
  <child2/>
</root>

可以看到新树和旧树是分开的;这是因为 lxml 将 parent 存储在元素中,因此不能重用它们 - 它必须为每个 child.

创建新元素

ElementTree 不会在元素中保留 parent,因此同一元素可能同时存在于多个树中。据我所知,没有 built-in 强制深度复制的方法...... deepcopyelement.copy() 都做与 copy 完全相同的事情 - 他们复制节点,然后将其连接到原始节点的 children。因此,对副本的更改会更改原件 - 而不是您想要的。

我发现使它正常工作的最简单方法就是简单地序列化为一个字符串,然后再次反序列化它。这会强制创建全新的元素。它很慢 - 但它也总是有效。比较以下方法:

import xml.etree.ElementTree as etree
e = Element('root')
etree.SubElement(e, 'child1')
etree.SubElement(e, 'child2')

#f = copy(e)
#f[0].tag = 'newchild1'
# If you do the above, the first child of e will also be 'newchild1'
# So that won't work. 

# Simple method, slow but complete
In [194]: %timeit f = etree.fromstring(etree.tostring(e))
10000 loops, best of 3: 71.8 µs per loop

# Faster method, but you must implement the recursion - this only
# looks at a single level.
In [195]: %%timeit
   .....: f = etree.Element('newparent')
   .....: f.extend([x.copy() for x in e])
   .....:
100000 loops, best of 3: 9.49 µs per loop

这个底层方法确实创建了first-level children的副本,而且比第一个版本快很多。但是,这仅适用于单层嵌套;如果其中任何一个有 children,你也必须自己去复制它们。您也许可以编写递归副本,而且速度可能更快;我做这件事的地方还没有 performance-sensitive 所以我没有在我的代码中打扰。 tostring/fromstring 例程效率相当低,但很简单,而且无论树有多深,它都能正常工作。