I'm trying to select all books that are not reserved now:
Answer:
/library/all/book[not(text() = /library/reserved/book/text())]
My previous answer did not work in all cases (see @MichaelKay's comment).
The reason why my struck-out answer does not work is because the predicate is trying to find at least one node in the node-set selected with reserved/book/text()
with a text that is not equal to the current text(). This is different from finding a book where there does not exist a reserved book with the same text.
The predicate in my new answer will attempt to find a reserved book with the same text, and upon failing to do so (whether there are no reserved books or none of the reserved books have the same text, not()
will negate the false
result and return the book that is not reserved.
See this answer on another question for further explanation.
From the spec:
If one object to be compared is a node-set and the other is a string, then the comparison will be true if and only if there is a node in the node-set such that the result of performing the comparison on the string-value of the node and the other string is true.
The !=
operator will attempt to return true
if it finds at least one node with a different text. The =
operator will attempt to return true
if at least one node has the same text. Both will return false
if the node-set is empty. Therefore, the solution is to return true
if there are no reserved books (empty node-set), or if none of the reserved books have the same text, ergo not(text() = /library/reserved/book/text())
.
If it is guaranteed that no two <book>
elements in each list will have the same text content, then this will work:
/library/all/book[text() != ../../reserved/book/text()]