Sunday, August 05, 2007

Building XML the Groovy way in Java

When working with XML - i. e. creating XML documents - the Java DOM API is a little cumbersome. You have to ask all sorts of factories for instances of themselves, those instances for documents, elements and so forth. It is usually a lot code to write, even if all you want is a little XML fragment with only a few elements, e. g. to be sent over the network to some server. One way to make your life easier is to resign to StringBuilder/StringBuffer and building the XML "by hand". However this is error-prone and not always easy to read.

Recently I had to implement a service that responded with XML over the net, building the document by collecting data from several sources and combining them together. The first version I wrote used the DOM API and once finished was hard to read even for me. I would have liked to use Groovy's MarkupBuilder for this, however company policy does not allow this (yet).

So I looked around for a similarly easy to read solution in plain Java. I found this entry in The Ancient Art Of Programming. It discusses the problem of the verbosity of the Java solution and compares it to the clarity of .NET's rather new Linq feature. In one of the comments Erik-Jan Blanksma posted an implementation that mimics the syntax using variable argument lists. I took the classes and built a second version of my code with it. Now the Java code looks almost like the Groovy MarkupBuilder code, except that I do not need any additional libraries. Take a look at this example:

private String getResult() {
XElement tResult = new XElement("result",
new XElement("error", "0"),
new XElement("receipt",
new XElement("head",
new XElement("id", adapter.getId()),
new XElement("ctry", adapter.getCountryCode()),
new XElement("region", adapter.getRegionCode()),
new XElement("primary", adapter.isPrimary())
)
);
return tResult.toString();
}

This is really great to use. I added some tweaks to the code to make it more robust and suitable for my needs. Apart from adding some error checking (no empty element names, null values etc.) I also created an interface for classes that convert a java.util.Collection into a corresponding array of XElements. This was necessary, because the original version did not allow dynamic additions to the XML. This interface is intended to allow worker classes that somewhat mimic the Groovy syntax of just intermixing loops etc. A simple implementation could look like this:

public class SimpleWorker implements XCollectionWorker<String> {
public XElement[] work(Collection<String> aCollection) {
List<XElement> tList = new ArrayList<XElement>();
for (String tString : aCollection) {
tList.add(new XElement("string", tString));
}
return tList.toArray(EMPTY_XXELEMENT_ARRAY);
}
}

Using it to add a list of Strings to an XML document can be done like this:

private String getResult(Collection<String> aStringCollection) {
XElement tResult = new XElement("result",
new XElement("error", "0"),
new XElement("receipt",
new XElement("head",
new XElement("id", adapter.getId()),
new XElement("ctry", adapter.getCountryCode()),
new XElement("region", adapter.getRegionCode()),
new XElement("primary", adapter.isPrimary())
),
new XElement("someStrings", aStringCollection, new SimpleWorker())
)
);
return tResult.toString();
}

I have packed the necessary files into this ZIP archive. Feel free to use them, if they suit your needs. If you make any improvements, I would be happy to hear from you.

1 comment:

Jason said...

Nice bit of code. I actually used it on a project I am working on and tweaked it to support an element that has attributes and nested elements. If you would like that update I would like to send it as a sign of gratitude.

Thanks for posting it and saving me some time and frustration.

Jas