Thursday, August 7, 2008

Grails, Groovy, XML

Last few days, while working on grails tutorials, I needed to translate some Grails domain objects to XML. I thought it should not be so hard as there is lot of articles about Groovy and XML. But what I found out is that most of the articles present only the simple usage of Groovy XmlSlurper. One of the examples would be:

def xml = new groovy.xml.MarkupBuilder()
xml.person(id:99){
firstname("John" )
lastname("Smith" )
}

And what I need is to traverse recursive structure of domain object. I know it is not so hard but anyway I decided to describe it here. At the end, as you will see it is very simple. But, by the way I will also explain how Grails supports XML transformations.

Let start with Grails support. If you don't request special structure of XML but it is enough to directly transform domain object to XML then Grails is excellent solution for you.

Domain object to translate is:

class LearningArea {
String title
String description
Date dateCreated
Date lastUpdated
List tutorials
List subAreas

static hasMany = [tutorials: TutorialLink, subAreas: LearningArea]

static constraints = {
title(blank:false, minSize:3, maxSize:500)
description(blank:false, maxSize:3000)
}

public String toString() {
return title
}
}

In Grails you can translate object to XML as in following example:

LearningArea learningArea = LearningArea.get(1)
String xml = render learningArea as XML

But interesting thing to notice is that render can be imported from two packages:

grails.converters.XML and grails.converters.deep.XML

And of course there is difference which package you use.

If you use deep package complete domain objects tree (with all sub areas's, sub areas and so on...) will be generated with all the properties. So in the case of LearningArea complete tree of all subAreas and all tutorials will be translated to XML including all properties.

If you use just converters.XML package, top level object is fully translated but referenced objects are translated only to the level of ids. This means that XML generated will be much smaller but to obtain additional information you need to retrieve additional data later.

Unfortunately for me, I couldn't use this Grails XML magic because I didn't want tutorials in the XML but I wanted full tree of subAreas only with titles and without description properties. So I had to use Groovy XmlSlurper. Important thing to notice in the above example of XmlSlurper is that when using XML builder, you can also provide closures. So all I needed was to recursively traverse the tree of the subAreas. And the code to do that is very simple:

    def generateXML= {
def writer = new StringWriter()
MarkupBuilder xml = new MarkupBuilder(writer)

LearningArea lr = LearningArea.findByTitle("Groovy")

toXml(lr, xml)

String str = writer.toString()
}

private void toXml(LearningArea la, def xml) {
xml.learningArea(name: la.title, id: la.id) {
la.subAreas.each {
toXml(it, xml)
}
}
}

As we can see all the logic is in the toXml method. This method accepts instance of LearningArea and MarkupBuilder, writes element learningArea to XML with attributes name and id, and the as subelements recursively writes all sub learning areas. This way the complete XML tree is generated in few lines of code and I got example that is little bit more complex than most of the examples on the web.

And if you didn't visited grails tutorials yes, you can do it now :)

5 comments:

freaker said...

Great tutorial. Thanks!

blog said...

Even though this post is over a year old, it's still useful. :) The combination of builders and recursion is really powerful! I used it to transform a hierarchy of Java objects (of unknown depth) into nested HTML ul elements.

Thanks!
Matt

Steve said...

Thanks. I was wondering how to get the whole object tree!!

Anonymous said...

Thanks,
Although this requires a bit more manual work (you have to write out each property yourself), the increase of control you get for using this method instead of the ill-documented and flawed XML.deep method is well worth the effort (which isn't even that much of an effort)

Erli Quintana said...

Great explanation about xml converts

Thanks :)