Groovy Operators

Control Statements

Groovy File Handling

Groovy Error & Exceptions

Groovy Multithreading

Groovy Synchronization

Groovy - Closure based Quering XML



Groovy provides a very powerful and idomatic ways to query XML. We can use XmlSlurper which leverages heavily closures for querying XML. We can use filter, each and collect methods which accepts closure to define the quering logic.

Load and Parse existing XML document

Using XmlSlurper we can load the XML document in the application.

def xmlMovies = '''
<movies>
   <movie title = 'Enemy Behind'> 
      <type>War, Thriller</type> 
      <format>DVD</format> 
      <year>2003</year>
      <director>John Adam</director>	  
      <rating>8</rating> 
      <stars>PG</stars> 
   </movie> 
   <movie title = 'War Zone'> 
      <type>War, Thriller</type> 
      <format>DVD</format> 
      <year>2013</year>
      <director>John Carry</director>	  
      <rating>4</rating> 
      <stars>PG</stars> 
   </movie> 
   <movie title = 'Transformers'> 
      <type>Anime, Science Fiction</type> 
      <format>DVD</format> 
      <year>1989</year>
	  <director>Cooper</director>
      <rating>7</rating> 
      <stars>R</stars> 
   </movie>
   <movie title = 'Godzilla Plus One'> 
      <type>Anime, Science Fiction</type> 
      <format>DVD</format> 
      <year>2024</year>
	  <director>Cooper</director>
      <rating>9</rating> 
      <stars>R</stars> 
   </movie>   
</movies>
'''

// parse xml into movies object
def movies = new XmlSlurper().parseText(xmlMovies)

// We can parse XML file as well
// def movies = new XmlSlurper().parse(new File('movies.xml'))

Filter Movies by Rating

// Using a closure with 'findAll' to filter movies by ratings
def highRatedMovies = movies.movie.findAll { movie ->
    movie.rating.toBigDecimal() > 7
}

highRatedMovies.each { println "Highly Rated Moview: ${it.@title.text()} (${it.rating.text()})" }

Collect Movies Titles with Directors

// Using a closure with 'collect' to get titles with directors
def titlesWithDirectors = movies.movie.collect { movie ->
    "${movie.@title.text()} by ${movie.director.text()}"
}

println "Titles with directors: $titlesWithDirectors"

Iterating Movies using each

// Using a closure with 'each' to perform an action on each movie
movies.movie.each { movie ->
   println "Type: ${movie.type}, Title: ${movie.@title}"
}

In above examples, the closures {movie -> ...} define the implementation logic to filter, transform and process each movie object. Closure receives GPathResult which represents the movie element and we can access elements and attibutes of the movie element in the closure.

Complete Example - Quering XML using Closures

def xmlMovies = '''
<movies>
   <movie title = 'Enemy Behind'> 
      <type>War, Thriller</type> 
      <format>DVD</format> 
      <year>2003</year>
      <director>John Adam</director>	  
      <rating>8</rating> 
      <stars>PG</stars> 
   </movie> 
   <movie title = 'War Zone'> 
      <type>War, Thriller</type> 
      <format>DVD</format> 
      <year>2013</year>
      <director>John Carry</director>	  
      <rating>4</rating> 
      <stars>PG</stars> 
   </movie> 
   <movie title = 'Transformers'> 
      <type>Anime, Science Fiction</type> 
      <format>DVD</format> 
      <year>1989</year>
	  <director>Cooper</director>
      <rating>7</rating> 
      <stars>R</stars> 
   </movie>
   <movie title = 'Godzilla Plus One'> 
      <type>Anime, Science Fiction</type> 
      <format>DVD</format> 
      <year>2024</year>
	  <director>Cooper</director>
      <rating>9</rating> 
      <stars>R</stars> 
   </movie>   
</movies>
'''

// parse xml into movies object
def movies = new XmlSlurper().parseText(xmlMovies)

// Using a closure with 'findAll' to filter movies by ratings
def highRatedMovies = movies.movie.findAll { movie ->
    movie.rating.toBigDecimal() > 7
}

highRatedMovies.each { println "Highly Rated Moview: ${it.@title.text()} (${it.rating.text()})" }

// Using a closure with 'collect' to get titles with directors
def titlesWithDirectors = movies.movie.collect { movie ->
    "${movie.@title.text()} by ${movie.director.text()}"
}

println "Titles with directors: $titlesWithDirectors"

// Using a closure with 'each' to perform an action on each movie
movies.movie.each { movie ->
   println "Type: ${movie.type}, Title: ${movie.@title}"
}

Output

When we run the above program, we will get the following result.

Highly Rated Moview: Enemy Behind (8)
Highly Rated Moview: Godzilla Plus One (9)
Titles with directors: [Enemy Behind by John Adam, War Zone by John Carry, Transformers by Cooper, Godzilla Plus One by Cooper]
Type: War, Thriller, Title: Enemy Behind
Type: War, Thriller, Title: War Zone
Type: Anime, Science Fiction, Title: Transformers
Type: Anime, Science Fiction, Title: Godzilla Plus One
Advertisements