In my job I have more often than not, have to write software to integrate two or more systems. This, often, means writing code that reads an XML stream onto a Java object for manipulation and eventually writes the result into another XML stream.
This typically involves creating binding objects for the input XML and the output XML and then a sequence of calls to getters on the input object and setters on output object to implement the mapping.
This task is very error prone and tedious. A good solution is using the
MarkupBuilder
(and, yes, I know about XSLT
, but this is not the point here!)So, suppose that you have the following POJO
and you want to serialize the instance
public class Contact {
private String id;
private String name;
private String surname;
// ...
}
into the following XML
Contact c = new Contact();
c.setId("123");
c.setName("John");
c.setSurname("Bloggs");
<contact>
<key>123</key>
<firstname>John</firstname>
<secondname>Bloggs</secondname>
</contact>
Note the difference between the POJO attribute names and the XML tag names
Using groovy and its
MarkupBuilder
, the first thing to do is to create a groovy converter, a class with a method that reads the data from the bean and produces the XML. Create a file src/groovy/ContactConverter.groovy
with the following code:class ContactConverter{
def convert(bean){
def writer = new StringWriter();
def xml = new MarkupBuilder();
xml.contact {
firstname(bean.name)
secondname(bean.surname)
}
writer.toString()
}
}
If you execute the code
println new COntactConverter().convert(c)
in a groovy shell, you get the XML shown above.
The next step is then to make this code executable from your Java service.
You can use this class (an adaptation of the code available in the groovy documentation):
public class GroovyConverterInvoker {
public String invoke(String fileName, Object bean){
GroovyObject groovyObject = createObjectFromStreamName(strName);
String result = (String)groovyObject.invokeMethod("convert", new Object[]{bean});
return result;
}
public GroovyObject createObjectFromStreamName(String fileName) {
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
Class groovyClass;
File f = new File(fileName);
try {
groovyClass = loader.parseClass(f);
return (GroovyObject)groovyClass.newInstance();
} catch (CompilationFailedException e) {
throw new IllegalStateException("Unable to compile " + fileName, e);
} catch (IOException e) {
throw new IllegalStateException("Unable to open file " + fileName, e);
} catch (InstantiationException e) {
throw new IllegalStateException("Unable instantiate object for class in " + fileName, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unable access object of class in " + fileName, e);
}
}
}
You can then invoke the conversion:
public String invokeContactConverter(Object bean){
String fName = "src/groovy/ContactConverter.groovy";
return new GroovyConverterInvoker().invoke(fName, bean);
}
You can now use JAXB/XStream or XmlBeans to parse the result of
invokeContactConverter
and initialize a new POJO for further use, or send the XML on the wire.The solution can be generalised and optimised to a point where you only need to write groovy converters and load/modify them dynamically when necessary.