XPath选择包含特定列表子集的元素

3

您好!假设以下是XML:

<store>
    <book id="b1"></book>
    <book id="b2"></book>
</store>
<store>
    <book id="b2"></book>
    <book id="b4"></book>
</store>
<booklist>
    <book id="b1"></book>
    <book id="b2"></book>
    <book id="b3"></book>
</booklist>

我希望编写一个Xpath查询,可以选择所有书籍均在booklist中的商店。在我的示例中,第一个商店符合条件,但第二个商店不符合条件。
我尝试了以下查询://store[./book/@id = /booklist/book/@id],但它会选择至少包含1本公共书籍的商店,而不是全部都符合条件的商店。
此外,我没有找到任何类似于"contains"的谓词,可以用于两个节点列表,所以我缺乏想法。
编辑:非常感谢任何帮助,但我更喜欢使用“基本”的Xpath功能来解决问题(如果存在)。这是一种新的语言。还是感谢所有回答我的人。

好问题,+1。看看我的答案,这是迄今为止最短、最简单的一行XPath表达式解决方案。同时也提供了解释。 - Dimitre Novatchev
4个回答

4

用途:

/*/store[not(book[not(@id = /*/booklist/*/@id)])]

当应用于此XML文档(提供的文档,通过将其包装在单个顶级元素中使其成为格式良好的):

<t>
    <store>
        <book id="b1"></book>
        <book id="b2"></book>
    </store>
    <store>
        <book id="b2"></book>
        <book id="b4"></book>
    </store>
    <booklist>
        <book id="b1"></book>
        <book id="b2"></book>
        <book id="b3"></book>
    </booklist>
</t>

所选的stor元素

<store>
   <book id="b1"/>
   <book id="b2"/>
</store>

使用XSLT作为XPath主机的验证方法:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "/*/store[not(book[not(@id = /*/booklist/*/@id)])]"/>
 </xsl:template>
</xsl:stylesheet>

应用此 XSLT 转换到上述 XML 文档时,将产生所需的正确结果。
<store>
   <book id="b1"/>
   <book id="b2"/>
</store>

说明:

表达式:

/*/store[not(book[not(@id = /*/booklist/*/@id)])]

意思是:

选择所有顶层元素的子元素 store,其中没有一个子元素的 id 属性值是 booklistbook 子元素的 id 属性值之一。


1

下面是一个可行的解决方案

//store[not(book/@id = //store/book[not(@id = //booklist/book/@id)]/@id)]

应用于此 XML(为测试清晰起见添加了name),它选择了商店13
<xml>
  <store name="1">
    <book id="b1"/>
    <book id="b2"/>
  </store>
  <store name="2">
    <book id="b2"/>
    <book id="b4"/>
  </store>
  <store name="3">
    <book id="b1"/>
    <book id="b3"/>
  </store>
  <store name="4">
    <book id="b3"/>
    <book id="b4"/>
  </store>
  <booklist>
    <book id="b1"/>
    <book id="b2"/>
    <book id="b3"/>
  </booklist>
</xml>

解释(两个否定构成肯定)

//store[not(book/@id = # get me all stores that don't have a book/@id that is in the list below
//store/book[not(@id = //booklist/book/@id)]/@id) # get me a list of store/book/@id where @id is not in the book list
]

是的,这就是我在寻找的解决方案。谢谢你。 - TryHarder

0

你可能需要使用函数,试试这个:

for $bid in //booklist/book/@id 
  if(every $id in //store/book/@id satisfies $id=$bid ) then fn:true()
  else fn:false()

不确定它是否有效,但只是给你一个想法。


这是一种有趣的方法。由于我对Xpath还不熟悉,这是我第一次接触其中的函数。 - TryHarder

0

我认为使用exslt集合扩展是可能的

E.g.:

//store[count(./book/@id) = count(set:intersection(./book/@id,//booklist/book/@id))]

但是在Python的lxml.etree中,intersection似乎并不像我期望的那样工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接