Предикат XPath с подпутями с lxml? - PullRequest
7 голосов
/ 02 июня 2011

Я пытаюсь понять и XPath, который был отправлен мне для использования с формами ACORD XML (общий формат в страховании). Отправленный мне XPath (сокращен для краткости):

./PersApplicationInfo/InsuredOrPrincipal[InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd="AN"]/GeneralPartyInfo

Я сталкиваюсь с проблемами в том, что lxml библиотека Python говорит мне, что [InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd="AN"] - это invalid predicate. Я не могу найти нигде в спецификации XPath предикатов , которая идентифицирует этот синтаксис, чтобы я мог изменить этот предикат для работы.

Есть ли документация о том, что именно этот предикат выбирает? Кроме того, это даже допустимый предикат, или что-то где-то искажено?

Возможно, связано:

Я считаю, что компания, с которой я работаю, является магазином MS, так что этот XPath может быть действительным в C # или каком-либо другом языке в этом стеке? Я не совсем уверен.

Обновление:

По запросу комментария, здесь есть некоторая дополнительная информация.

Пример XML:

<ACORD>
  <InsuranceSvcRq>
    <HomePolicyQuoteInqRq>
      <PersPolicy>
        <PersApplicationInfo>
            <InsuredOrPrincipal>
                <InsuredOrPrincipalInfo>
                    <InsuredOrPrincipalRoleCd>AN</InsuredOrPrincipalRoleCd>
                </InsuredOrPrincipalInfo>
                <GeneralPartyInfo>
                    <Addr>
                        <Addr1></Addr1>
                    </Addr>
                </GeneralPartyInfo>
            </InsuredOrPrincipal>
        </PersApplicationInfo>
      </PersPolicy>
    </HomePolicyQuoteInqRq>
  </InsuranceSvcRq>
</ACORD>

Пример кода (с полным XPath вместо фрагмента):

>>> from lxml import etree
>>> tree = etree.fromstring(raw)
>>> tree.find('./InsuranceSvcRq/HomePolicyQuoteInqRq/PersPolicy/PersApplicationInfo/InsuredOrPrincipal[InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd="AN"]/GeneralPartyInfo/Addr/Addr1')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "lxml.etree.pyx", line 1409, in lxml.etree._Element.find (src/lxml/lxml.etree.c:39972)
  File "/Library/Python/2.5/site-packages/lxml-2.3-py2.5-macosx-10.3-i386.egg/lxml/_elementpath.py", line 271, in find
    it = iterfind(elem, path, namespaces)
  File "/Library/Python/2.5/site-packages/lxml-2.3-py2.5-macosx-10.3-i386.egg/lxml/_elementpath.py", line 261, in iterfind
    selector = _build_path_iterator(path, namespaces)
  File "/Library/Python/2.5/site-packages/lxml-2.3-py2.5-macosx-10.3-i386.egg/lxml/_elementpath.py", line 245, in _build_path_iterator
    selector.append(ops[token[0]](_next, token))
  File "/Library/Python/2.5/site-packages/lxml-2.3-py2.5-macosx-10.3-i386.egg/lxml/_elementpath.py", line 207, in prepare_predicate
    raise SyntaxError("invalid predicate")
SyntaxError: invalid predicate

Ответы [ 4 ]

18 голосов
/ 03 июня 2011

Измените tree.find на tree.xpath. find и findall присутствуют в lxml для обеспечения совместимости с другими реализациями ElementTree. Эти методы не реализуют весь язык XPath . Чтобы использовать выражения XPath, содержащие более сложные функции, используйте метод xpath, класс XPath или XPathEvaluator.

Например:

import io
import lxml.etree as ET

content='''\
<ACORD>
  <InsuranceSvcRq>
    <HomePolicyQuoteInqRq>
      <PersPolicy>
        <PersApplicationInfo>
            <InsuredOrPrincipal>
                <InsuredOrPrincipalInfo>
                    <InsuredOrPrincipalRoleCd>AN</InsuredOrPrincipalRoleCd>
                </InsuredOrPrincipalInfo>
                <GeneralPartyInfo>
                    <Addr>
                        <Addr1></Addr1>
                    </Addr>
                </GeneralPartyInfo>
            </InsuredOrPrincipal>
        </PersApplicationInfo>
      </PersPolicy>
    </HomePolicyQuoteInqRq>
  </InsuranceSvcRq>
</ACORD>
'''
tree=ET.parse(io.BytesIO(content))
path='//PersApplicationInfo/InsuredOrPrincipal[InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd="AN"]/GeneralPartyInfo'
result=tree.xpath(path)
print(result)

выходы

[<Element GeneralPartyInfo at b75a8194>]

в то время как tree.find дает

SyntaxError: invalid node predicate
3 голосов
/ 02 июня 2011

Твой пример, на мой взгляд, прекрасно. Я бы проверил, имеет ли реализация XPath lxmls какие-то задокументированные ограничения или что-то в этом роде.

1 голос
/ 03 июня 2011
./PersApplicationInfo/InsuredOrPrincipal
                 [InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd="AN"]
                     /GeneralPartyInfo/

Несколько проблем с этим выражением :

  1. Конечный символ / делает его синтаксически недействительным . Это отмечает начало нового шага местоположения, но ничего не следует.

  2. Как заметил доктор Майкл Кей, у вас могут быть проблемы с вложенными кавычками в Python.

Предлагаемое решение :

./PersApplicationInfo/InsuredOrPrincipal
                 [InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd='AN']
                     /GeneralPartyInfo

В этом выражении двойные кавычки заменяются одинарными кавычками. Второе изменение - удаление завершающего символа /.

Обновление : Теперь OP предоставил более полный пример кода, и я могу убедиться, что с используемым выражением XPath все в порядке. Ниже приведена проверка с помощью XSLT:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:template match="/*">
  <xsl:copy-of select=
  './InsuranceSvcRq/HomePolicyQuoteInqRq/PersPolicy
                 /PersApplicationInfo/InsuredOrPrincipal
                     [InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd="AN"]
                                                   /GeneralPartyInfo/Addr/Addr1'/>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному документу XML :

<ACORD>
    <InsuranceSvcRq>
        <HomePolicyQuoteInqRq>
            <PersPolicy>
                <PersApplicationInfo>
                    <InsuredOrPrincipal>
                        <InsuredOrPrincipalInfo>
                            <InsuredOrPrincipalRoleCd>AN</InsuredOrPrincipalRoleCd>
                        </InsuredOrPrincipalInfo>
                        <GeneralPartyInfo>
                            <Addr>
                                <Addr1></Addr1>
                            </Addr>
                        </GeneralPartyInfo>
                    </InsuredOrPrincipal>
                </PersApplicationInfo>
            </PersPolicy>
        </HomePolicyQuoteInqRq>
    </InsuranceSvcRq>
</ACORD>

желаемый, правильный результат выдается :

<Addr1 />

Заключение : Проблема либо в использовании кода Python, либо (менее вероятно) в используемом движке XPath, есть ошибка.

0 голосов
/ 03 июня 2011

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

...