81
votes

I have some XML that is structured like this:

<whatson>
  <productions>    
    <production>
      <category>Film</category>
    </production>
    <production>
      <category>Business</category>
    </production>
    <production>
      <category>Business training</category>
    </production>
  </productions>
</whatson>

And I need to select every production with a category that doesn't contain "Business" (so just the first production in this example).

Is this possible with XPath? I tried working along these lines but got nowhere:

//production[not(contains(category,'business'))]
4

4 Answers

148
votes

XPath queries are case sensitive. Having looked at your example (which, by the way, is awesome, nobody seems to provide examples anymore!), I can get the result you want just by changing "business", to "Business"

//production[not(contains(category,'Business'))]

I have tested this by opening the XML file in Chrome, and using the Developer tools to execute that XPath queries, and it gave me just the Film category back.

27
votes

I need to select every production with a category that doesn't contain "Business"

Although I upvoted @Arran's answer as correct, I would also add this... Strictly interpreted, the OP's specification would be implemented as

//production[category[not(contains(., 'Business'))]]

rather than

//production[not(contains(category, 'Business'))]

The latter selects every production whose first category child doesn't contain "Business". The two XPath expressions will behave differently when a production has no category children, or more than one.

It doesn't make any difference in practice as long as every <production> has exactly one <category> child, as in your short example XML. Whether you can always count on that being true or not, depends on various factors, such as whether you have a schema that enforces that constraint. Personally, I would go for the more robust option, since it doesn't "cost" much... assuming your requirement as stated in the question is really correct (as opposed to e.g. 'select every production that doesn't have a category that contains "Business"').

5
votes

You can use not(expression) function

not() is a function in xpath (as opposed to an operator)

Example:

//a[not(contains(@id, 'xx'))]

OR

expression != true()
0
votes

Should be xpath with not contains() method, //production[not(contains(category,'business'))]