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.
4 comments:
And in Scala you do it like this
class Person(
val key: Int,
val name: String,
val secondName: String
)
{
def toXml =
<contact>
<key> { key } </key>
<firstname> {name } </firstname>
<secondname> { secondName } </secondname>
</contact>
}
@jau -- okay you scala smartass -- in Grails (which sits atop groovy):
p = new Person(key:blah, name:"bob", secondName:"smartass")
render p as XML
I promised myself to look at Scala a bit closer.
@jau: the approach shown in is similar to any other template technology (jsp, velocity, ...): they all suffer of the same painful problems: mixing xml syntax and native language, lack of IDE support. I find XmlSlurper+MarkupBuilder neater
@anonymous: Grails approach (which can be implemented in Groovy as well with few meta-programming) does't solve my problem. That is mapping
class Customer{
def id
def name
def surname
}
into
<client>
<key>...</key>
<firstname>...</firstname>
<secondname>...</secondname>
</client>
Note the difference between the attribute name and the tag name
In Groovy you can also have templates as in Scala, see here: http://groovy.codehaus.org/Groovy+Templates
In your code you are missing the key in the result XML.
And it is only the half of the solution, see http://tatiyants.com/xml-transformation-performance-groovy-vs-xslt/
Post a Comment