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

时间:2011-06-24 08:49:02

标签: xml xpath

美好的一天!假设以下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查询,它将选择商店 把他们所有的书放在书单中。在我的例子中,它是第一个商店,但不是第二个商店 我试过了 //store[./book/@id = /booklist/book/@id]
但它选择的商店至少包含一本普通书籍,而不是所有书籍 另外,没有为两个节点列表找到任何类似“包含”的谓词,所以我缺乏想法。

编辑:我非常感谢任何帮助,但我更喜欢使用“基本”Xpath功能(如果存在)的解决方案。这对我来说是一种新语言。但是,感谢所有回复的人。

4 个答案:

答案 0 :(得分: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>

当对上述XML文档应用此XSLT转换时,会生成所需的正确结果

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

<强>解释

表达式:

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

表示:

选择所有store元素(top元素的子元素),使得book属性的值为id之一的id(子)中只有一个book 1}}属性booklist的{​​{1}} s(子)的属性值。

答案 1 :(得分:1)

这是一个可行的解决方案

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

应用于此xml(为了清晰测试而添加商店name),它选择商店1&amp; 3

<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
]

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:0)

我想认为可以使用exslt set extensions

E.g:

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

但是intersection似乎没有按照我期望的那样在python的lxml.etree

中完成