доступ к родительскому узлу узла ElementTree

Я использую встроенный модуль Python ElementTree. Получить доступ к дочерним узлам просто, но как насчет родительских или одноуровневых узлов? - можно ли это сделать эффективно, не обходя все дерево?


person hoju    schedule 31.01.2010    source источник
comment
См. stackoverflow.com /вопросы/374245/   -  person kennytm    schedule 31.01.2010


Ответы (10)


Прямой поддержки в виде атрибута parent нет, но, возможно, вы можете использовать шаблоны, описанные здесь. для достижения желаемого эффекта. Предлагается следующая однострочная строка (обновленная из сообщения со ссылкой на Python 3.8) для создания сопоставления дочерних и родительских элементов для всего дерева с использованием метода xml.etree.ElementTree.Element.iter:

parent_map = {c: p for p in tree.iter() for c in p}
person Vinay Sajip    schedule 31.01.2010
comment
Обновление синтаксиса, 2017 г. / python3 parent_map = {(c,p) for p in tree.iter( ) for c in p} - person gerardw; 08.09.2017
comment
Исправление: parent_map = {c:p for p in root.iter( ) for c in p} - person gerardw; 08.09.2017
comment
Что, если вы не можете прочитать весь XML-файл за один раз, но должны перебирать файл с помощью iter()? - person fpsdkfsdkmsdfsdfm; 03.08.2020

Ответ Виная все еще должен работать, но для Python 2.7+ и 3.2+ рекомендуется следующее:

parent_map = {c:p for p in tree.iter() for c in p}

getiterator() устарел в пользу iter(), и приятно использовать новый конструктор понимания списка dict.

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

parent_map = {}
for p in tree.iter():
    for c in p:
        if c in parent_map:
            parent_map[c].append(p)
            # Or raise, if you don't want to allow this.
        else:
            parent_map[c] = [p]
            # Or parent_map[c] = p if you don't want to allow this
person supergra    schedule 21.11.2013
comment
Что делать, если у вас нет доступа к дереву? Как после .find() - person Brett; 27.07.2015
comment
Я не знаю никакого способа получить корневой узел (и, следовательно, родителей/предков), если вы не сохранили ссылку на него. Но я не понимаю, какое отношение к этому имеет .find(). - person supergra; 28.07.2015
comment
я только что использовал .find() в качестве примера функции, которая просто возвращает элемент - person Brett; 30.07.2015

Вы можете использовать нотацию xpath ... в ElementTree.

<parent>
     <child id="123">data1</child>
</parent>

xml.findall('.//child[@id="123"]...')
>> [<Element 'parent'>]
person josven    schedule 22.10.2015
comment
Это фантастическое решение, работает с find(), даже если вы знаете, что ищете только один элемент. Вот так: root.find(".//*[@testname='generated_sql']...") - person Bostone; 08.09.2017
comment
Я ничего не смог найти об этом синтаксисе ... XPath. Что оно делает? Документы на него есть? - person raphinesse; 23.05.2018
comment
Выражение @raphinesse ... взято из XPath 1.0. Библиотека Python Std имеет ограниченную поддержку выражений XPath, lxml имеет большую поддержку. - person josven; 22.08.2018
comment
Код в ответе работает, но я нигде не могу найти ссылку на этот синтаксис с тремя точками. Это не упоминается в рекомендации XPath 1.0. - person mzjn; 09.04.2020
comment
Как насчет элементов, у которых нет атрибута id? - person Ioannis Filippidis; 16.09.2020
comment
@ioannis-filippidis О, вам просто нужен действительный XPath, за которым следует ... Вы можете использовать любой атрибут Все дочерние элементы: xml.findall('.//child...') Какой-то другой атрибут: xml.findall('.//child[@other="123"]...') - person josven; 17.09.2020
comment
Внимание: этот код отлично работает только с двумя точками. Синтаксиса с тремя точками не существует. Это не в документах, как упоминалось другими. Это просто комбинация . (выбрать текущий узел) и .. (получить родителя). - person Nat Riddle; 20.05.2021

Как указано в Получить родительский элемент после использования метода поиска (xml .etree.ElementTree) вам придется выполнить непрямой поиск родителя. Имея xml:

<a>
 <b>
  <c>data</c>
  <d>data</d>    
 </b>
</a>

Предполагая, что вы создали элемент etree в переменной xml, вы можете использовать:

 In[1] parent = xml.find('.//c/..')
 In[2] child = parent.find('./c')

В результате чего:

Out[1]: <Element 'b' at 0x00XXXXXX> 
Out[2]: <Element 'c' at 0x00XXXXXX>

Родитель более высокого уровня будет найден как: secondparent=xml.find('.//c/../..') являющийся <Element 'a' at 0x00XXXXXX>

person Vaasha    schedule 24.11.2015

Селектор XPath '..' нельзя использовать для извлечения родительского узла в 3.5.3 или 3.6.1 (по крайней мере, в OSX), например, в интерактивном режиме:

import xml.etree.ElementTree as ET
root = ET.fromstring('<parent><child></child></parent>')
child = root.find('child')
parent = child.find('..') # retrieve the parent
parent is None # unexpected answer True

Последний ответ разбивает все надежды...

person jlaurens    schedule 04.07.2018

Вставка сюда моего ответа из https://stackoverflow.com/a/54943960/492336:

У меня была аналогичная проблема, и я немного креативил. Оказывается, ничто не мешает нам самим добавить информацию о происхождении. Мы можем позже раздеть его, как только он нам больше не понадобится.

def addParentInfo(et):
    for child in et:
        child.attrib['__my_parent__'] = et
        addParentInfo(child)

def stripParentInfo(et):
    for child in et:
        child.attrib.pop('__my_parent__', 'None')
        stripParentInfo(child)

def getParent(et):
    if '__my_parent__' in et.attrib:
        return et.attrib['__my_parent__']
    else:
        return None

# Example usage

tree = ...
addParentInfo(tree.getroot())
el = tree.findall(...)[0]
parent = getParent(el)
while parent:
    doSomethingWith(parent)
    parent = getParent(parent)
stripParentInfo(tree.getroot())
person sashoalm    schedule 01.03.2019

Получил ответ от

https://towardsdatascience.com/processing-xml-in-python-elementtree-c8992941efd2

Совет: используйте '...' внутри XPath, чтобы вернуть родительский элемент текущего элемента.


for object_book in root.findall('.//*[@name="The Hunger Games"]...'):
    print(object_book)
person Skanda    schedule 03.01.2021
comment
Это тот же ответ, что и stackoverflow.com/a/33280875/407651. - person mzjn; 06.01.2021
comment
stackoverflow.com/a/33280875/407651 не говорит, что ... есть этот ответ. - person Ted Shaneyfelt; 05.07.2021

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

parent_node = next(child_node.iterancestors())

Это вызовет исключение StopIteration, если у элемента нет предков, поэтому будьте готовы поймать его, если вы можете столкнуться с таким сценарием.

person Shadow    schedule 04.12.2014

Другой способ, если вам просто нужен родительский элемент одного подэлемента, а также известный xpath подэлемента.

parentElement = subElement.find(xpath+"/..")
person MK at Soho    schedule 23.02.2014
comment
У меня не работает, я получаю "Нет" - то же самое, если я просто использую subElement.find('..'). - person damian; 21.01.2015
comment
Предполагается, что переменная с именем xpath уже существует, поэтому большинству пользователей она бесполезна. - person ArtOfWarfare; 22.05.2020

Посмотрите на 19.7.2.2. раздел: Поддерживаемый синтаксис XPath ...

Найдите родителя узла, используя путь:

parent_node = node.find('..')
person Alf    schedule 13.12.2017
comment
Вы проверяли это? Если вам удалось заставить его работать, опубликуйте полный пример кода, демонстрирующий это. См. этот комментарий: stackoverflow.com/questions /2170610/ - person mzjn; 14.12.2017
comment
В документации Python 3 говорится: Возвращает None, если путь пытается достичь предков начального элемента (вызван элемент find). (docs.python.org/3/library /). - person mzjn; 14.12.2017
comment
Работает на меня. Самый лучший и содержательный ответ. - person ToTenMilan; 06.02.2018