Попытка перебрать узлы text (), чтобы найти своих родителей - PullRequest
2 голосов
/ 23 января 2020

Предположим, у меня есть следующий пример XML

<a>some <b>text</b> example</a>

Использование l xml позвольте мне запросить текстовые узлы несколькими различными способами:

>>> xml.xpath("//text()")
['some ', 'text', ' example']
>>> xml.xpath("//a/text()")
['some ', ' example']
>>> xml.xpath("//b/text()")
['text']

Пока все хорошо (все это хорошо напечатанные lxml.etree._ElementUnicodeResult объекты). Но теперь я хотел бы перейти к родительским узлам этих текстовых узлов:

>>> xml.xpath("//text()/parent::*")
[<Element a at 0x10aa383c0>, <Element b at 0x10aa38410>]
>>> xml.xpath("//a/text()/parent::*")
[<Element a at 0x10aa383c0>]
>>> xml.xpath("//b/text()/parent::*")
[<Element b at 0x10aa38410>]
>>> xml.xpath("//*/text()/parent::*")
[<Element a at 0x10aa383c0>, <Element b at 0x10aa38410>]

Как обычные операции XPath, они работают, и я получаю наборы результатов родительских узлов обратно. Но я бы хотел перебрать текстовые узлы и , а затем получить их соответствующих родителей:

>>> for e in xml.xpath("//text()"):
...     print(e.xpath("./parent::*"))
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'lxml.etree._ElementUnicodeResult' object has no attribute 'xpath'

Хм, ладно, похоже, это деталь реализации l xml или даже базовый libxml2 ? Но getparent() на помощь ... или?

>>> for e in xml.xpath("//text()"):
...     print(f"'{e}'", e.getparent())
... 
'some ' <Element a at 0x10aa383c0>
'text' <Element b at 0x10aa38410>
' example' <Element b at 0x10aa38410>

И вот что меня озадачивает: хотя во время поиска сверху вниз родительские узлы имели смысл для всех текстовых узлов, если снизу вверх, то они разные: текстовый узел ' example' имеет родителя a или b, в зависимости от того, как я запрашиваю его родителя.

Я что-то упустил или это ошибка ( возможно, из-за .text и .tail узлов элементов ?

Добавление

После рассмотрения соответствующих l xml исходный код Я думаю, что внутреннее _elementStringResultFactory необходимо исправить, см. Ошибку 1859435 . Между тем, следующий обходной путь дает ожидаемые результаты:

>>> for e in xml.xpath("//text()"):
...     p = e.getparent()
...     print(f"'{e}'", p.getparent() if e.is_tail else p)
... 
'some ' <Element a at 0x110799460>
'text' <Element b at 0x1100ad230>
' example' <Element a at 0x110799460>

Ответы [ 2 ]

1 голос
/ 27 января 2020

Согласно комментарию Стефана к сообщению об ошибке l xml (помечено как "Не исправлю" сейчас):

Я считаю это особенность. […]

Возможно, «родитель» - не идеальное слово. Может быть, там может быть лучшая документация или дополнительный пример в документации. c PR приветствуется, но я не думаю, что поведение должно измениться.

Итак, в то время как это конкретное поведение l xml отличается от стандартного XML (см. Markus ' комментарий ) предназначен. Код, который я использовал в Addendum выше, я полагаю, является правильным способом доступа к родительскому узлу текстовых узлов (т.е. проверяет, являются ли они tail или нет):

>>> for e in xml.xpath("//text()"):
...     p = e.getparent()
...     print(f"'{e}'", p.getparent() if e.is_tail else p)
0 голосов
/ 26 января 2020

Я создал объект xml следующим образом:

txt = '<a>some <b>text</b> example</a>'
xml = etree.fromstring(txt)

Чтобы напечатать все текстовые узлы и их родителей (имена тегов), выполните следующие действия:

  1. Определить функцию, получающую список родителей данного узла :

    def getParents(node):
        rv = []
        if node.is_tail:
            node = node.getparent()
        while True:
            node = node.getparent()
            if node is None:
                return rv
            rv.append(node.tag)
    
  2. Распечатать результат (содержимое каждого текстового узла и его патенты), работает:

    for node in xml.xpath("//text()"):
        print(node, getParents(node))
    

Для вашего XML образца я получил:

some  ['a']
text ['b', 'a']
 example ['a']
...