Frage

How can I replace an element during iteration in an elementtree? I'm writing a treeprocessor for markdown and would like to wrap an element.

<pre class='inner'>...</pre>

Should become

<div class='wrapper'><pre class='inner'>...</pre></div>

I use getiterator('pre') to find the elements, but I don't know how to wrap it. The trouble point is replacing the found element with the new wrapper, but preserving the existing one as the child.

War es hilfreich?

Lösung

This is a bit of a tricky one. First, you'll need to get the parent element as described in this previous question.

parent_map = dict((c, p) for p in tree.getiterator() for c in p)

If you can get markdown to use lxml, this is a little easier -- I believe that lxml elements know their parents already.

Now, when you get your element from iterating, you can also get the parent:

for elem in list(tree.getiterator('pre')):
    parent = parent_map[elem]
    wrap_elem(parent, elem)

Note that I've turned the iterator from the tree into a list -- We don't want to modify the tree while iterating over it. That could be trouble.

Finally, you're in position to move the element around:

def wrap_elem(parent, elem)
    parent_index = list(parent).index(elem)
    parent.remove(elem)
    new_elem = ET.Element('div', attrib={'class': 'wrapper'})
    parent.insert(parent_index, new_elem)
    new_elem.append(elem)

*Note that I haven't tested this code exactly... let me know if you find any bugs.

Andere Tipps

In my experience, you can use the method below to get what you want:
xml.etree.ElementTree.SubElement( I will just call it ET.Subelement) http://docs.python.org/2/library/xml.etree.elementtree.html#xml.etree.ElementTree.SubElement

Here is the steps:
Before your iteration, you should get the parent element of these iterated element first, store it into variable parent.

Then,
1, store the element <pre class='inner'>...</pre> into a variable temp

2, add a new subelement div into parent:

div = ET.SubElement(parent, 'div')

and set the attrib of div:

div.set('class','wrapper')

3, add the element in step 1 as a subelement of div,

ET.SubElement(div, temp)  

4, delete the element in step 1:

parent.remove(temp)

Something like this works for one:

for i, element in enumerate(parent):
    if is_the_one_you_want_to_replace(element):
        parent.remove(element)
        parent.insert(i, new_element)
        break

Something like this works for many:

replacement_map = {}

for i, element in enumerate(parent):
    if is_an_element_you_want_to_replace(element):
        replacement_map[i] = el_to_remove, el_to_add

for index, (el_to_remove, el_to_add) in replacement_map.items():
    parent.remove(el_to_remove)
    parent.insert(index, el_to_add)

Another solution that works for me, similar to lyfing's. Copy the element into a temp; retag the original element with the wanted outer tag and clear it, then append the copy into the original.

import copy

temp = copy.deepcopy(elem)
elem.tag = "div"
elem.set("class","wrapper")
elem.clear()
elem.append(temp)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top