Tuesday 9 April 2013

Groovy Xml Series: Templating

I guess in many web applications there're times when you are going to return the same XML estructure with little changes every time. So you would be looking for a general template with some placeholders to substitute at runtime.

How to this with Groovy: groovy.text.XmlTemplateEngine to the rescue :-)

The following example's been inspired in an entry of MrHaki talking about the topic. The first we have to build is the template.

Template


     def tpl = ''' 
          
              
                  
                      books.eachWithIndex{book,index->
                                      
                               
                              ${book.title}    
                                        
                                   
                                  book.author
                              
                          
                      }
                  
              
          
      '''

Several things to take into account when creating the template:

  • Notice that I've used single quotes. If I had used double quotes I'd have been in trouble at runtime because Groovy would have tried to substitute the placeholders beforehand.
  • Adding "gsp" namespace: Adding this namespace we'll be able to use scriptlet and expression tags. I'll explain both later.
  • Groovy expressions: Like if we were using GString expressions we can use expressions like ${index}. Again notice all variables are evaluated differently than GString instances.

Scriptlets


Basically we're going to use the tag when trying to build complex estructures or looping through a  set of values. In the example:

    books.eachWithIndex{book,index->
        // Nested code
    }

Here we were looping through a list of books. Notice how we're going to be able to access to the variables created in that snippet inside the scope of the tag.

Expressions


Following the documentation the tag should be used for code fragments which produce output. In this example we want to add the book's author as the text value of the author's node.

              
       book.author
    

Groovy Expressions


We can be using the expressions ${expression} and $variable to add expressions to the document.

   ${book.title} 

Invoking XmlTemplateEngine


Finally we should put all pieces together and set all variables in the template. For that we will be invoking the XmlTemplateEngine instance and passing a map with the variables we want to substitute in the template.

This is a method taken from a Spock specification created for testing the XmlTemplateEngine functionality.

     def "Creating a new xml using the template and some bindings"(){
          setup: "Building some data"
              def books = (1..10).collect{
                  [title:"Book${it}",author:"Author${it}"]
              }
          when: "Creating the engine instance and compiling the template"
              def engine = new XmlTemplateEngine()
              def templ = engine.createTemplate(tpl)
          and: "Binding the values with the template"
              def bindings = [books:books]
          and: "Parsing the template with the included bindings"
              def writable = templ.make(bindings)
           /* I want to see the output in the test report */
              println writable
          and: "Parsing the result to check the outcoming xml"
              def response = new XmlSlurper().parseText(writable.toString())
          then: "We should have a document with 10 books"
              response.value.books.book.size() == 10
      }

See how first we create a template instance from the XmlTemplateEngine instance.

def engine = new XmlTemplateEngine()
     def templ = engine.createTemplate(tpl)

Then we pass the books as a parameter to the template. That will give us a writable instance.

      def bindings = [books:books]
      def writable = templ.make(bindings)

At the end we only parse the document again for testing purposes.

      def response = new XmlSlurper().parseText(writable.toString())
      response.value.books.book.size() == 10

Resources

1 comment:

  1. thank you for this mario - sure liked the ideas of single quote marks and gsp namespace usages. Must bookmark this pg so i can try it again soon
    thx
    jim

    ReplyDelete