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

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

12 ответов

Решение

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

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

Ответ Vinay должен работать, но для 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

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

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

xml.findall('.//child[@id="123"]...')
>> [<Element 'parent'>]

Как упоминалось в разделе "Получить родительский элемент" после использования метода find (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>

Вставьте сюда мой ответ от /questions/35351600/poluchit-roditelskij-element-posle-ispolzovaniya-metoda-find-xmletreeelementtree/35351618#35351618:

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

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())

Селектор 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

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

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

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

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

      
for object_book in root.findall('.//*[@name="The Hunger Games"]...'):
    print(object_book)

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

parent_node = next(child_node.iterancestors())

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

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

parentElement = subElement.find(xpath+"/..")

Большинство решений, опубликованных на данный момент

  • либо используйте XPath… но Python вообще не поддерживает поиск предков с помощью XPath ( см. комментарий ),
  • или выполнить постобработку всего дерева после его построения (например, этот ответ или тот )… но это требует анализа и построения всего дерева, что может быть нежелательно для больших данных XML (например, дампов Википедии).

Если вы анализируете XML постепенно, скажем, с помощью xml.etree.ElementTree.iterparseили xml.etree.ElementTree.XMLPullParser, вы можете отслеживать текущий путь (от корневого узла до текущего узла), отслеживая открытие и закрытие тегов (startиendсобытия). Пример:

      import xml.etree.ElementTree as ET

current_path = [ ]

for event, elem in ET.iterparse('test.xml', events=['start', 'end']):
    # opening tag:
    if event == 'start':
        current_path.append(elem)
    # closing tag:
    else:
        assert event == 'end'
        assert len(current_path) > 0 and current_path[-1] is elem
        current_path.pop()
        parent = current_path[-1] if len(current_path) > 0 else None
        # `elem` is the current element (fully built),
        # `parent` is its parent (some of its children after `elem`
        # might not have been parsed yet)
        #
        # ... do something ...

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

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

parent_node = node.find('..')
      import xml.etree.ElementTree as ET

f1 = "yourFile"

xmlTree = ET.parse(f1)

for root in xmlTree.getroot():
    print(root.tag)
Другие вопросы по тегам