Car Example

This example assumes the following class is already on your CLASSPATH:

Using Xalan

Here is an example of using Apache Xalan with Groovy to read an existing XML file:

// require(groupId:'xalan', artifactId:'xalan', version:'2.6.0')
import org.apache.xpath.XPathAPI
import javax.xml.parsers.DocumentBuilderFactory

messages = []

def processCar(car) {
    def make = XPathAPI.eval(car, '@make').str()
    def country = XPathAPI.eval(car, 'country/text()').str()
    def type = XPathAPI.eval(car, 'record/@type').str()
    messages << make + ' of ' + country + ' has a ' + type + ' record'
}

def builder     = DocumentBuilderFactory.newInstance().newDocumentBuilder()
def inputStream = new ByteArrayInputStream(XmlExamples.CAR_RECORDS.bytes)
def records     = builder.parse(inputStream).documentElement

XPathAPI.selectNodeList(records, '//car').each{ processCar(it) }

assert messages == [
    'Holden of Australia has a speed record',
    'Peel of Isle of Man has a size record',
    'Bugatti of France has a price record'
]

Using Jaxen

Here is an example of using Jaxen with Groovy to read an existing XML file:

// require(groupId:'jaxen', artifactId:'jaxen', version:'1.1-beta-10')
import org.jaxen.dom.DOMXPath
import javax.xml.parsers.DocumentBuilderFactory

messages = []

def processCar(car) {
    def make = new DOMXPath('@make').stringValueOf(car)
    def country = new DOMXPath('country/text()').stringValueOf(car)
    def type = new DOMXPath('record/@type').stringValueOf(car)
    messages << make + ' of ' + country + ' has a ' + type + ' record'
}

def builder     = DocumentBuilderFactory.newInstance().newDocumentBuilder()
def inputStream = new ByteArrayInputStream(XmlExamples.CAR_RECORDS.bytes)
def records     = builder.parse(inputStream).documentElement

new DOMXPath('//car').selectNodes(records).each{ processCar(it) }

assert messages == [
    'Holden of Australia has a speed record',
    'Peel of Isle of Man has a size record',
    'Bugatti of France has a price record'
]

Note: many libraries (e.g. DOM4J, JDOM, XOM) bundle or provide optional support for Jaxen. You may not need to download any additional JARs to use it.

Using Java's native XPath support

If you are using Java 5 and above, you don't need to use the standalone Xalan jar but instead can use the now built-in XPath facilities:

import javax.xml.xpath.*

def xpath = XPathFactory.newInstance().newXPath()

def nodes = xpath.evaluate( '//car', records, XPathConstants.NODESET )
nodes.each{ 
  def make = xpath.evaluate( '@make', it )
  def country = xpath.evaluate( 'country/text()', it )
  def type = xpath.evaluate( 'record/@type', it )
  messages << "$make of $country has a $type record"
}

People in Groups Example

Inspired by this example, here is how to use XPath to determine which groups include all three of alan, paul and ivan for this graph representing group membership.


Picture source: http://stage.vambenepe.com/pages/graph.png

First, here is the XML data capturing the group membership information:

def xml = '''
<doc>
  <person name="alan"><g>1</g><g>2</g><g>4</g></person>
  <person name="marc"><g>1</g><g>2</g><g>3</g></person>
  <person name="paul"><g>2</g><g>3</g><g>4</g></person>
  <person name="ivan"><g>2</g><g>4</g></person>
  <person name="eric"><g>4</g></person>
</doc>
'''

Using Java's native XPath support

Here is how to check that groups 2 and 4 are the groups in question using the built-in XPath capabilities of Java 5 and above:

import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.xpath.*

xpath = '''
  /doc/person[@name="alan"]/g
  [.=/doc/person[@name="paul"]/g]
  [.=/doc/person[@name="ivan"]/g]
'''

builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
doc     = builder.parse(new ByteArrayInputStream(xml.bytes))
expr    = XPathFactory.newInstance().newXPath().compile(xpath)
nodes   = expr.evaluate(doc, XPathConstants.NODESET)
assert  nodes.collect { node -> node.textContent } == ['2', '4']

And you can refactor your XPath Expression with something like:

import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.xpath.*

groupsOf = { name -> "/doc/person[@name='$name']/g" }
xpath    = "${groupsOf 'alan'}[.=${groupsOf 'paul'}][.=${groupsOf 'ivan'}]"
builder  = DocumentBuilderFactory.newInstance().newDocumentBuilder()
doc      = builder.parse(new ByteArrayInputStream(xml.bytes))
expr     = XPathFactory.newInstance().newXPath().compile(xpath)
nodes    = expr.evaluate(doc, XPathConstants.NODESET)
assert   nodes.collect { node -> node.textContent } == ['2', '4']

Using GPath

Of course, you can also do this without XPath by using Groovy's GPath facilities as follows:

def root = new XmlParser().parseText(xml)
Set groups = root.person.g.collect{ it.text() }
assert groups.findAll{ group ->
  root.person.g.findAll{
    it.text() == group
  }.'..'.'@name'.containsAll(['alan', 'paul', 'ivan'])
} == ['2', '4']