
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, 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.