JAXB can be a real time saver when working on a project that uses XSD to describe interfaces implemented in Java. Sometimes, however, the generated code is not up to standard. I ran into a problem that seems very common and found a very elegant solution to it on the web.
Yesterday, while working on the XML Schema for Document, I had to decide whether I would wrap collections in a surrounding element, like this:
[xml]
<Document>
…
<Replies>
<Reply>
…
</Reply>
<Reply>
…
</Reply>
…
</Replies>
…
</Document>
[/xml]
or I would just have a sequence of <Reply>-elements directly under Document. I think the general consensus is to use wrapping elements, but I decided to base the design on another measure: What would the (Java) code look like that would be generated from the Schema by JAXB.
After configuring the maven-jaxb-plugin into the project (which was really easy and it integrated out of the box with Eclipse through M2Eclipse), the Java code was a little disappointing: For every collection I would get the following structure:
[java]
class Document {
@XmlElement(name = "Replies")
private Replies replies;
public Replies getReplies() {
return replies;
}
public class Replies() {
@XmlElement(name = "Reply")
private List<Reply> reply;
public List<Reply> getReply() {
return reply;
}
}
}
[/java]
This would lead to the following usage pattern:
[java]
Document doc;
…
doc.getReplies().getReply().add(new Reply(…));
[/java]
which is in my opinion quite awful. I would expect there to be a simple collection structure:
[java]
Document doc;
…
doc.getReplies().add(new Reply(…));
[/java]
But whether I would use the wrapping element or not, I would still get the additional class.
There is a JAXB annotation that will help in this situation:
[java]
@XmlElementWrapper(name = "replies")
@XmlElement(name = "reply")
protected List<Reply> replies;
[/java]
And this is exactly what we want, but how to tell the JAXB code generator to use that annotation?
After some googling around, I found this blog posting (www.conspicio.dk/blog/bjarne/jaxb-xmlelementwrapper-plugin), that provides a solution by using a custom plugin for XJB (the JAXB code generator). Configuring this in maven was quite easy:
- Add the plugin jar (which was unfortunately not available from a Maven repo) to the maven-jaxb-plugin dependencies.
- Add an <args>-element to the plugin configuration with the -Xxew option to activate the plugin.
The final Maven configuration looks like this:
[xml]
<plugin>
<groupId>com.sun.tools.xjc.maven2</groupId>
<artifactId>maven-jaxb-plugin</artifactId>
<executions>
<execution>
<id>jaxb-generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<includeSchemas>
<includeSchema>xsd/Document.xsd</includeSchema>
</includeSchemas>
<strict>true</strict>
<verbose>true</verbose>
<args>-Xxew</args>
</configuration>
<dependencies>
<dependency>
<groupId>xew</groupId>
<artifactId>xew</artifactId>
<version>1.0.0</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
[/xml]
The generated code now uses a simple collection, so it looks much more usable.
One last problem is that the generated Replies class, that is now no longer used, is not removed from the code: A little clean up task that must have slipped the plugin developers mind.