51

How to clone Element objects in Python xml.etree? I'm trying to procedurally move and copy (then modify their attributes) nodes.

Ming-Tang
  • 17,410
  • 8
  • 38
  • 76

7 Answers7

65

You can just use copy.deepcopy() to make a copy of the element. (this will also work with lxml by the way).

Steven
  • 28,002
  • 5
  • 61
  • 51
21

A different, and somewhat disturbing solution:

new_element = lxml.etree.fromstring(lxml.etree.tostring(elem))
Ali Afshar
  • 40,967
  • 12
  • 95
  • 109
2

If you have a handle on the Element elem's parent you can call

new_element = SubElement(parent, elem.tag, elem.attrib)

Otherwise you might want to try

new_element = makeelement(elem.tag, elem.attrib)

but this is not advised.

Niel de Wet
  • 7,806
  • 9
  • 63
  • 100
  • @SHiNKiROU You can compare `id(old_element)` with `id(new_element)` to see if it actually creates a different object in memory. Does this help? – Niel de Wet Oct 23 '10 at 21:19
  • As @Ming-Tang mentions, this **does not** copy the children. – halloleo Feb 16 '16 at 02:58
  • 2
    This is useful when you want to copy an `Element` and its attributes, but you do not want to copy the children (for example when reconstructing a strict subtree by iterating through an element's ancestors). It is portable to *lxml.etree*, because unfortunately with *lxml.etree*, `copy.copy()` also copies children (documented, but how is this different from a deepcopy?). – davidA Jul 20 '16 at 00:52
1

At least in Python 2.7 etree Element has a copy method: http://hg.python.org/cpython/file/2.7/Lib/xml/etree/ElementTree.py#l233

It is a shallow copy, but that is preferable in some cases.

In my case I am duplicating some SVG Elements and adding a transform. Duplicating children wouldn't serve any purpose since where relevant they already inherit their parent's transform.

kitsu.eb
  • 2,996
  • 1
  • 26
  • 28
  • 6
    For anyone using this and thinking of replacing *xml.etree.ElementTree* with *lxml.etree* in the future, note that `Element.copy()` does not exist in *lxml.etree*, and `copy.copy()` copies children too, when applied to an `lxml.etree.Element`. – davidA Jul 20 '16 at 00:48
  • Does not work either with cElementTree (Python 2.7). So prefer copy.copy() (shallow copy) or copy.deepcopy() for code evolutivity. – Thierry Oct 09 '17 at 15:29
0

If you procedurally move through your tree with loops, you can use insert to clone directly ( insert(index, subelement) ) and tree indexing (both in the documentation):

import xml.etree.ElementTree as ET
mytree = ET.parse('some_xml_file.xml')  # parse tree from xml file
root = mytree.getroot()  # get the tree root
    
for elem in root:  # iterate over children of root
   if condition_for_cloning(elem) == True:      
      elem.insert(len(elem), elem[3])  # insert the 4th child of elem to the end of the element (clone an element)  

or for children with some tag:

for elem in root:
   children_of_interest = elem.findall("tag_of_element_to_clone")
   elem.insert(len(elem), children_of_interest[1])
RowMow
  • 11
  • 3
-1

For anyone visiting from the future:

If you want to clone the entire element, use append.

new_tree = ET.Element('root')
for elem in a_different_tree:
    new_tree.append(elem)

@dennis-williamson made a comment about it which I overlooked and eventually stumbled on the answer here https://stackoverflow.com/a/6533808/4916945

ron_g
  • 1,474
  • 2
  • 21
  • 39
  • To be clear, this will remove the element from the original tree! – Spencer May 22 '22 at 22:01
  • 2
    @Spencer it won't remove anything.. but it will make a copy-by-reference, not a clone. So any changes to the new element will alter the original – john k Jan 04 '23 at 16:15
-2

For future reference.

Simplest way to copy a node (or tree) and keep it's children, without having to import ANOTHER library ONLY for that:

def copy_tree( tree_root ):
    return et.ElementTree( tree_root );

duplicated_node_tree = copy_tree ( node );    # type(duplicated_node_tree) is ElementTree
duplicated_tree_root_element = new_tree.getroot();  # type(duplicated_tree_root_element) is Element
DarkLighting
  • 309
  • 2
  • 10
  • 1
    To be clear, this is not a deep copy. (Yes, the post says "and keep its children," but I still felt the need to test what it meant.) – harpo Aug 17 '15 at 20:42