Как заменить элемент в дереве? (дерево элементов/уценка)

Как я могу заменить элемент во время итерации в дереве элементов? Я пишу древовидный процессор для уценки и хотел бы обернуть элемент.

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

Должен стать

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

Я использую getiterator('pre') для поиска элементов, но не знаю, как его обернуть. Проблемой является замена найденного элемента новой оболочкой, но сохранение существующей в качестве дочерней.


person edA-qa mort-ora-y    schedule 05.01.2014    source источник


Ответы (4)


Это немного сложно. Во-первых, вам нужно получить родительский элемент, как описано в предыдущем вопросе.

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

Если вы можете получить уценку для использования lxml, это немного проще — я считаю, что элементы lxml уже знают своих родителей.

Теперь, когда вы получаете свой элемент от итерации, вы также можете получить родителя:

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

Обратите внимание, что я превратил итератор из дерева в список — мы не хотим изменять дерево во время итерации по нему. Это может быть проблемой.

Наконец, вы можете перемещать элемент:

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)

*Обратите внимание, что я точно не тестировал этот код... дайте мне знать, если вы обнаружите какие-либо ошибки.

person mgilson    schedule 05.01.2014
comment
Похоже, это должно сработать. Хотя вместо этого я попробовал вариант выполнения двойной итерации, перебор всех родителей, а затем внутренний цикл дочерних элементов. Я не хотел, чтобы во втором/более позднем проходе не были установлены все родители. - person edA-qa mort-ora-y; 05.01.2014


Что-то вроде этого работает для одного:

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

Что-то вроде этого работает для многих:

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)
person Caveman    schedule 31.01.2020

Еще одно решение, которое работает для меня, похожее на lyfing. Скопируйте элемент во временный файл; повторно пометьте исходный элемент нужным внешним тегом и очистите его, а затем добавьте копию в оригинал.

import copy

temp = copy.deepcopy(elem)
elem.tag = "div"
elem.set("class","wrapper")
elem.clear()
elem.append(temp)
person Eric Karnowski    schedule 19.09.2017