Следите за родительскими элементами с помощью ElementTree

Вот мой XML:

<beans>
    <property name = "type1">
        <list>
            <bean class = "bean1">
                <property name = "typeb">
                    <value>foo</value>
                </property>
            </bean>
            <bean class = "bean2">
                <property name ="typeb">
                    <value>bar</value>
                </property>
            </bean>
        </list>
    </property>

    <property name = "type2">
        <list>
            <bean class = "bean3">
                <list>
                    <property name= "typec">
                        <sometags/>
                    </property>
                    <property name= "typed">
                        <list>
                            <value>foo</value>
                            <value>bar</bar>
                        </list>
                    </property> 
               </list>


            </bean>
        </list>
    </property>
</beans>

Теперь нам нужно просмотреть это и удалить следующие элементы:

            <bean class = "bean1">
                <property = "typeb">
                    <value>foo</value>
                </property>
            </bean>

И:

            <value>foo</value>

(из класса свойств = «типизированный» элемент).

Теперь, чтобы добиться этого, я хотел бы сделать что-то вроде этого:

for element in root.iter('value'):
    if element.text == 'foo':
        p1= element.getParent()
        if p1.tag == 'list': #second case scenario, remove just the value tag. 
            p1.remove(element)
        else: #first case scenario - remove entire bean
            p2 = p1.getParent()
            p3 = p2.getParent()
            p3.remove(p2)

Однако ElementTree не поддерживает дочерний элемент, видящий его родительский элемент.

Каким может быть эффективный способ добиться этого? Учитывая, что это глубокая структура XML, мне не очень нравится идея рекурсивной функции, которая проверяет типы тегов на каждом уровне.


person dwjohnston    schedule 05.02.2014    source источник


Ответы (3)


с ElementTree используйте родителя, чтобы найти соответствующий дочерний элемент:

>>> parent = root.find('.//bean[@class="bean1"]')
>>> parent
<Element 'bean' at 0x10eb31550>
>>> parent.find('.//value').text
'foo'
person Guy Gavriely    schedule 05.02.2014

Вот как я решил это:

#gives you a list of every parent,child tuple
def iterparent(tree):
    for parent in tree.getiterator():
        for child in parent:
            yield parent, child

#recursive function. Deletes the given child node, from n parents back. 
#If n = 0 it deletes just the child. 
def removeParent(root, childToRemove, n):

    for parent, child in iterparent(root):
        if (childToRemove == child):
            if n>0:
                removeParent(root, parent, n-1)
            else: 
                parent.remove(child)


for parent, child in iterparent(root):
    if (child.tag == 'value' and (child.text in valuesToDelete):
        if (parent.tag == 'list'):
            removeParent(root, child, 0)
        else:
            removeParent(root, child, 2)    

Это на самом деле довольно элегантно. Мне это нравится.

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

person dwjohnston    schedule 05.02.2014

Модуль lxml.etree имеет метод getparent. Учитывая ваш пример XML (ну, после исправления несоответствующего закрывающего тега), я могу сделать это:

>>> from lxml import etree
>>> 
>>> with open('data.xml') as fd:
...     doc = etree.parse(fd)
... 
>>> matches = doc.xpath('//value[text()="foo"]')
>>> element = matches[0]
>>> etree.tostring(element)
'<value>foo</value>\n        '
>>> parent = element.getparent()
>>> print etree.tostring(element)
<value>foo</value>

>>> parent = element.getparent()
>>> print etree.tostring(parent)
<property name="typeb">
          <value>foo</value>
        </property>
>>> parent = parent.getparent()
>>> print etree.tostring(parent)
<bean class="bean1">
        <property name="typeb">
          <value>foo</value>
        </property>
      </bean>

..и так далее.

person larsks    schedule 05.02.2014