Multiple conditions (and operator) in XPath
Solution 1
It depends on the version of XPath.
You can't do this in a single XPath 1.0 expression because it would require a structure like
/bookstore/myBooks/book[/bookstore/books/book[
@title = _outer-predicate_/@title and
@author = _outer-predicate_/@author]]/@name
i.e. you'd need to capture the book being tested by the outer predicate so you can compare it to the inner one. Pure XPath 1.0 doesn't support this, you'd have to iterate over the content of myBooks
yourself (e.g. with an xsl:for-each
, as you've tagged your question "xslt").
In XPath 2.0 you can do it in one expression with an explicit quantifier
/bookstore/myBooks/book[not(some $book in /bookstore/books/book satisfies
($book/@author = @author and $book/@title = @title))]/@name
Solution 2
You can concatenate attribute values using concat()
and compare them:
//myBooks/book[not(concat(@*[name() = "title" or name() = "author"], ",") = concat(//books/book/@*[name() = "title" or name() = "author"], ","))]
For every book
in myBook
it gets the concatenation of title
and author
attributes and compares it with the same concatenation for every book
in books
.
Demo (using xmllint
):
$ xmllint input.xml --xpath '//myBooks/book[not(concat(@*[name() = "title" or name() = "author"], ",") = concat(//books/book/@*[name() = "title" or name() = "author"], ","))]'
<book name="Test2" title="Title 2" author="Author 1"/>
Solution 3
Assuming XSLT I would define a key <xsl:key name="book" match="books/book" use="concat(@title, '|', @author)"/>
and then in the predicate you can check [not(key('book', concat(@title, '|', @author)))]
.
![Cédric Bignon](https://i.stack.imgur.com/QrEhB.jpg?s=256&g=1)
Cédric Bignon
Updated on July 09, 2022Comments
-
Cédric Bignon almost 2 years
I have the following XML:
<bookstore> <books> <book name="book1" title="Title 1" author="Author 1"/> <book name="book2" title="Title 2" author="Author 2"/> <book name="book3" title="Title 1" author="Author 2"/> </books> <myBooks> <book name="Test1" title="Title 1" author="Author 1"/> <book name="Test2" title="Title 2" author="Author 1"/> <book name="Test3" title="Title 1" author="Author 2"/> </myBooks> </bookstore>
I want to get all
name
ofbook
inmyBooks
that have not a correspondingbook
inbooks
(title and author).So, for the example, I want to retrieve: the book
"Test2"
because the pair("Title 2", "Author 1")
does not exist inbooks
.So far, I have:
//myBooks/book[not(@title = //books/book/@title and @author = //books/book/@author)]
But, of course, in that case, the above XPath does not work because the combination ("Title 2", "Author 1") exists (from "book2" and "book1").
How can I apply the
and
operator on the same node? -
Cédric Bignon about 10 yearsThe XPath 2.0 version is exactly what I was looking for. Thanks.