etree Clone Node
Asked Answered
G

7

54

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

Geehan answered 23/10, 2010 at 20:43 Comment(0)
L
68

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

Luxurious answered 23/10, 2010 at 21:24 Comment(1)
That makes a copy, but it's not added to the tree. You'll need to do an append() or insert() to do that.Aitken
H
23

A different, and somewhat disturbing solution:

new_element = lxml.etree.fromstring(lxml.etree.tostring(elem))
Hectograph answered 20/11, 2012 at 1:14 Comment(3)
bahm, you just saved my life! This is really useful when replacing valuesBenzel
This is indeed a disturbing solution :)Pendley
Hah, love it! Indeed disturbing.Hoelscher
M
3

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.

Messinger answered 23/10, 2010 at 20:53 Comment(3)
@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?Messinger
As @Geehan mentions, this does not copy the children.Fleshings
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?).Shellashellac
H
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.

Hetero answered 28/5, 2013 at 19:31 Comment(2)
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.Shellashellac
Does not work either with cElementTree (Python 2.7). So prefer copy.copy() (shallow copy) or copy.deepcopy() for code evolutivity.Adjacent
V
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])
Virgy answered 4/8, 2021 at 17:44 Comment(0)
A
0

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://mcmap.net/q/340005/-can-39-t-dump-or-write-an-elementtree-element

Alkalinity answered 21/3, 2022 at 12:51 Comment(2)
To be clear, this will remove the element from the original tree!Hoelscher
@Hoelscher 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 originalExpressivity
C
-3

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
Claudclauddetta answered 24/11, 2014 at 16:28 Comment(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.)Watkins

© 2022 - 2024 — McMap. All rights reserved.