Доступ к родительскому узлу узла 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)