ProcedingSibling在XPath和Python中是如何工作的?它似乎显示了错误的输出

2024-09-28 17:02:13 发布

您现在位置:Python中文网/ 问答频道 /正文

对于XML数据

<X>
 <Y1>ABC1</Y1>
 <Y2>ABC2</Y2>
 <Z>
   <T>
     <R1>ABC3</R1>
     <R2>ABC4</R2>
   </T>
   <T>
     <R1>ABC5</R1>
     <R2>ABC6</R2>
   </T>
 </Z>
 <Y1>ABC7</Y1>
 <Y2>ABC8</Y2>
 <Z>
   <T>
     <R1>ABC3</R1>
     <R2>ABC9</R2>
   </T>
   <T>
     <R1>ABC5</R1>
     <R2>ABC9</R2>
   </T>
 </Z>
</X>

我编写了一个示例python文件,如下所示

from lxml import etree
tree = etree.parse('test.xml')
for i in tree.xpath("//X/Z/T[R1='ABC3']/parent::*/preceding-sibling::*"):
    print(i.tag, " - ", i.text)

我期望像这样的输出

Y1  -  ABC1
Y2  -  ABC2
Y1  -  ABC1
Y2  -  ABC2
Z  -  

Y1  -  ABC7
Y2  -  ABC8

但是收到了一个类似的

Y1  -  ABC1
Y2  -  ABC2
Z  -  

Y1  -  ABC7
Y2  -  ABC8

它应该打印所有前面的同级。对于“R1=ABC3”的第一个匹配,应打印Y1和Y2。对于“R1=ABC”的第二个匹配,它应该打印5个同级。总共应打印7个元素。 这里的错误是什么


Tags: 数据treexmletreer2r1y1y2
2条回答

XPath 1.0有一个节点集的概念,其中每个/步骤根据节点标识消除重复项,因此您使用的单个XPath表达式不会给出两次包含同一节点的集,任何重复项都会被消除

在XPath2.0中,当然/步骤操作符仍然具有相同的重复消除语义,但有一个更通用的序列概念,它使用for .. returnfor $p in //X/Z/T[R1='ABC3']/parent::* return $p/preceding-sibling::*)或XPath3.1!//X/Z/T[R1='ABC3']/parent::*!preceding-sibling::*)允许包含重复项,请参见https://xqueryfiddle.liberty-development.net/eiZQFoV

在XPath1.0中,您需要在宿主语言(例如Python)的循环中使用多个XPath求值,或者在Python中,您可以使用列表理解element_list = [el for parent in tree.xpath("//X/Z/T[R1='ABC3']/parent::*") for el in parent.xpath("preceding-sibling::*")]

问题被标记为xslt,但您没有使用XSLT。可以使用以下样式表实现预期输出:

XSLT1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8"/>

<xsl:template match="/X">
    <xsl:for-each select="Z[T/R1='ABC3']">
        <xsl:for-each select="preceding-sibling::*">
            <xsl:value-of select="name()" />
            <xsl:text> - </xsl:text>
            <xsl:value-of select="text()" />
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

正如Martin Honnen在回答中指出的那样,有必要分别处理每个匹配节点的前面兄弟节点,以获得两个单独的列表


还要注意,您的表达式:

Z/T[R1='ABC3']/parent::* 

是不必要的复杂:显然,匹配的T的父级必须是Z,因此您可以简单地编写:

Z[T/R1='ABC3']

相关问题 更多 >