поиск по пространству имен в lxml - PullRequest
0 голосов
/ 28 февраля 2020

У меня есть файл xml, элементы которого выглядят как gnc:account (это файл gnucash account). Я хочу найти все элементы с таким именем.

Однако, если я сделаю это;

for account in tree.iter('gnc:account'):
    print(account)

, я ничего не напечатаю. Вместо этого я написал этот нелепый кусок кода:

def n(string):
    pair = string.split(':')
    return '{{{}}}{}'.format(root.nsmap[pair[0]], pair[1])

И теперь я могу сделать это:

for account in tree.iter(n('gnc:account')):
    print(account)

, который работает.

Есть ли нелепость решение этой проблемы? Мне не интересно писать полный URI.

1 Ответ

2 голосов
/ 28 февраля 2020

То, что вы сейчас имеете, безусловно, слишком хаки sh, на мой взгляд.

Решение с XPath

Вы можете использовать XPath и зарегистрировать этот URI пространства имен и префикс:

>>> from io import StringIO
>>> s = """<root xmlns:gnc="www.gnc.com">
... <gnc:account>1</gnc:account>
... <gnc:account>2</gnc:account>
... </root>"""
>>> tree = etree.parse(StringIO(s))

# show that without the prefix, there are no results
>>> tree.xpath("//account")
[]

# with an unregistered prefix, throws an error
>>> tree.xpath("//gnc:account")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "src/lxml/etree.pyx", line 2287, in lxml.etree._ElementTree.xpath
  File "src/lxml/xpath.pxi", line 359, in lxml.etree.XPathDocumentEvaluator.__call__
  File "src/lxml/xpath.pxi", line 227, in lxml.etree._XPathEvaluatorBase._handle_result
lxml.etree.XPathEvalError: Undefined namespace prefix

# correct way of registering the namespace
>>> tree.xpath("//gnc:account", namespaces={'gnc': 'www.gnc.com'})
[<Element {www.gnc.com}account at 0x112bdd808>, <Element {www.gnc.com}account at 0x112bdd948>]

Придерживаясь tree.iter()

Если вы все еще хотели бы позвонить iter() таким образом, вам нужно будет следовать * 1016 Советы * l xml по использованию пространств имен с iter , например:

>>> for account in tree.iter('{www.gnc.com}account'):
...     print(account)
...
<Element {www.gnc.com}account at 0x112bdd808>
<Element {www.gnc.com}account at 0x112bdd948>

И если вы абсолютно хотите избежать записи URI пространства имен или регистрации пространства имен (чего я не делаю думаю, что это правильный аргумент, это довольно просто и понятно), вы также можете использовать

>>> for account in tree.iter('{*}account'):
...     print(account)
...
<Element {www.gnc.com}account at 0x112bdd808>
<Element {www.gnc.com}account at 0x112bdd948>
...