带有lxml的子路径的XPath谓词?

时间:2011-06-02 17:40:44

标签: python xml xpath lxml

我正在尝试理解和发送给我使用ACORD XML表单的XPath(保险中的通用格式)。他们发给我的XPath是(为简洁而截断):

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

我遇到麻烦的是,Python的lxml library告诉我[InsuredOrPrincipalInfo/InsuredOrPrincipalRoleCd="AN"]invalid predicate。我无法找到标识此语法的XPath spec on predicates中的任何位置,以便我可以修改此谓词。

是否有关于此谓词选择的文档?此外,这甚至是一个有效的谓词,还是在某个地方被破坏了?

可能相关:

我相信我正在使用的公司是一家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 个答案:

答案 0 :(得分:18)

tree.find更改为tree.xpath。 lxml中存在findfindall,以提供与ElementTree的其他实现的兼容性。 These methods do not implement the entire XPath language。要使用包含更多高级功能的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

答案 1 :(得分:3)

在我看来,你的榜样非常好。我会检查lxmls XPath实现是否有一些记录的限制或类似的东西。

答案 2 :(得分:1)

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

此表达式的一些问题

  1. 结尾/字符使语法无效。它标志着新位置步骤的开始,但没有任何内容。

  2. 正如Michael Kay博士所注意到的,你可能会遇到Python中嵌套引号的问题。

  3. 建议的解决方案

    ./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引擎有错误。

答案 3 :(得分:0)

你给出的XPath是完全正确的。也许问题出现在将其嵌入Python中,您需要使用Python转义约定来转义字符串中的双引号?