Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XPath select based on child value(s)

Tags:

xml

xpath

Here's some xml:

<books>
    <book>
        <author>Tom</author>
        <genres>
            <genre>Science</genre>
            <genre>Romance</genre>
        </genres>
    </book>
    <book>
        <author>Mike</author>
        <genres>
            <genre>Politics</genre>
            <genre>Romance</genre>
        </genres>
    </book>
</books>

How can I define an xpath such that it pulls all Romance books? Or maybe all Romance and Politics books?

like image 526
Jon Glazer Avatar asked Dec 07 '16 20:12

Jon Glazer


2 Answers

You can go with //book[./genres/genre = 'Romance'] and //book[./genres/genre = 'Romance' and ./genres/genre = 'Politics'], respectively

like image 163
Rubens Farias Avatar answered Sep 20 '22 03:09

Rubens Farias


All books in Romance genre:

//book[genres/genre = 'Romance']

All books in Romance and Politics genre:

//book[genres/genre = 'Romance' and genres/genre = 'Politics']

All books in Romance that are also in Politics genre (same as and above):

//book[genres/genre = 'Romance'][genres/genre = 'Politics']

All books in Romance or Politics genre:

//book[genres/genre = 'Romance' or genres/genre = 'Politics']

All books in Romance or Politics genre (XPath 2.0 only):

//book[genres/genre = ('Romance', 'Politics')]

Notes:

  1. //book finds all book elements anywhere beneath the root element; /books/book finds all book children of the books root element. For the given XML, they select the same elements.
  2. You can append /author to any of the above XPaths to select the author elements of the books of the specified criteria.

Having trouble making it work?

First, establish that a basic XPath works: //book should return two elements.

If it does not:

  1. Check spelling carefully of both the XPath expression and the XML.
  2. Check case. XML and XPath are case sensitive.
  3. Check that no namespaces are in play. Namespaces are effectively a part of the names of elements and attributes and must be accounted for. See How does XPath deal with XML namespaces?

Then, incrementally add XPath steps from there:

  1. //book[genres] should select book elements with any genres child element.

  2. //book[genres/genre] should select book elements with any genres child element, only if it in turn has a genre child element.

  3. //book[genres/genre = 'Romance'] should select all books in Romance genre, as requested. Note that 'Romance' must be quoted; otherwise, the expression would be testing against the string value of a Romance child element of book and will certainly fail.

like image 31
kjhughes Avatar answered Sep 23 '22 03:09

kjhughes