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.

有帮助吗?

解决方案

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.

其他提示

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)
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top