Using the javax.xml.bind annotations to convert Java objects to XML and XSD

4
Share this on .. Tweet about this on Twitter0Share on LinkedIn0Share on Facebook0Share on Google+0Email this to someoneShare on TumblrBuffer this page

Did you know that when you put the @XmlRootElement annotation on any class you can have an XML representation of that class?

When I first saw Java 6 I didn’t get very excited by it, but I have found out some nice things (scripting and Derby for example). This article will also describe a new feature of Java 6. I believe it was also possible with JWSDP, but I don’t like to search external libraries (or at least not standard in a Maven 2 repository ūüėČ )

I wanted to put an get an XML document in and from a database. With the new JDBC 4.0 you can save and load the XmlType data type from your database. But this article is not about JDBC, it started as an article about it, but then I discovered some pretty cool featues with XML only. The JDBC 4.0 has to wait a little longer.

Creating the XML document

When I was looking how to create an XmlType object I found out it was possible to marshal (the conversion from Object to XML representation) an object to an XML document. Let’s start with a simple class called Movie:

<code><span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">Movie </span><span style="color: #000000;">{</span>

<span style="color: #ffffff;">  </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">String title;</span>

<span style="color: #ffffff;">  </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">String genre;</span>

<span style="color: #ffffff;">  </span><span style="color: #7f0055;"><strong>private </strong></span><span style="color: #000000;">Integer year;</span>

<span style="color: #000000;">}</span></code>

Now create the getters and setters for these fields. Note that without getters and setters the marshalling doens’t work as expected! An empty constructor is also needed when you override the default constructor.

The final step is putting an @XmlRootElement annotation on your class.

<code><span style="color: #646464;">@XmlRootElement</span>

<span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">Movie </span><span style="color: #000000;">{</span>

<span style="color: #ffffff;">  </span><span style="color: #000000;">...</span>

<span style="color: #000000;">}</span></code>

I created a simple unit test to print an XML document to the System.out.

 

<code><span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">XmlTest </span><span style="color: #7f0055;"><strong>extends </strong></span><span style="color: #000000;">TestCase </span><span style="color: #000000;">{</span>
<span style="color: #ffffff;">  </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #7f0055;"><strong>void </strong></span><span style="color: #000000;">testXml</span><span style="color: #000000;">() </span><span style="color: #7f0055;"><strong>throws </strong></span><span style="color: #000000;">Exception </span><span style="color: #000000;">{</span>
<span style="color: #ffffff;">    </span><span style="color: #000000;">Movie movie = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">Movie</span><span style="color: #000000;">(</span><span style="color: #2a00ff;">"Pulp Fiction"</span><span style="color: #000000;">, </span><span style="color: #2a00ff;">"Action"</span><span style="color: #000000;">, </span><span style="color: #990000;">1994</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>

<span style="color: #ffffff;">    </span><span style="color: #000000;">JAXBContext context = JAXBContext.newInstance</span><span style="color: #000000;">(</span><span style="color: #000000;">Movie.</span><span style="color: #7f0055;"><strong>class</strong></span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #ffffff;">    </span><span style="color: #000000;">Marshaller marshaller = context.createMarshaller</span><span style="color: #000000;">()</span><span style="color: #000000;">;</span>
<span style="color: #ffffff;">    </span><span style="color: #000000;">marshaller.setProperty</span><span style="color: #000000;">(</span><span style="color: #000000;">Marshaller.JAXB_FORMATTED_OUTPUT, </span><span style="color: #7f0055;"><strong>true</strong></span><span style="color: #000000;">)</span><span style="color: #000000;">; </span><span style="color: #3f7f5f;">//pretty print XML</span>
<span style="color: #ffffff;">    </span><span style="color: #000000;">marshaller.marshal</span><span style="color: #000000;">(</span><span style="color: #000000;">movie, System.out</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #ffffff;">  </span><span style="color: #000000;">}</span>
<span style="color: #000000;">}</span></code>

I created a constructor for my Movie object, so I didn’t have to set 3 fields. The next line creates a JAXBContext, this is the entry point of all your JAXB operations. You can give an array of classes (or just use the Java 5 varargs as parameters) or a list of packages. For now it is enough to pass the Movie class. With our context we can create a Marshaller, the marshaller will perform the actual conversion to XML.

The property jaxb.formatted.output is used for debugging purposes only, normally all XML tags will be on one line.

Finally we invoke the marshal method and you’ll see the following document appear on the console:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<movie>
<genre>Action</genre>
<title>Pulp Fiction</title>
<year>1994</year>
</movie>

You can also route the output to other streams or objects. The javadocs of the Marshaller explain this pretty well.

To get a String create a StringWriter object and ivoke the getBuffer() get a StringBuffer, which can be toString()-ed to get a String.

 

Creating the schema

Whilst browsing the API I found a method called generateSchema. That sounds cool and easy. The easy part appeared to be a little more difficult, but it works.

The generateSchema method on JAXBContext takes a SchemaOutputResolver as parameter and changes that object (which is quite evil in my opinion). So let’s find an implementation of the SchemaOutputResolver. When I opened the Javadoc I saw this interesting line of comment : “This is a class, not an interface so as to allow future versions to evolve without breaking the compatibility” I don’t get this, an interface breaks compatibility? I looked up the source of SchemaOutputResolver and found no non-abstract methods, so that’s an interface to me, but you can’t extend from another class and extending from another class doesn’t break compatibility or does it?

But enough of that, I couldn’t find an implementation of the class. Fortunately google code search helped me out. In the source of Xfire I found an implementation of the clas:

<code><span style="color: #7f0055;"><strong>final </strong></span><span style="color: #000000;">List&lt;DOMResult&gt; results = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">ArrayList&lt;DOMResult&gt;</span><span style="color: #000000;">()</span><span style="color: #000000;">;</span>
<span style="color: #ffffff;">  </span><span style="color: #000000;">context.generateSchema</span><span style="color: #000000;">(</span>
<span style="color: #ffffff;">    </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">SchemaOutputResolver</span><span style="color: #000000;">() {</span>
<span style="color: #ffffff;">      </span><span style="color: #646464;">@Override</span>
<span style="color: #ffffff;">      </span><span style="color: #7f0055;"><strong>public </strong></span><span style="color: #000000;">Result createOutput</span><span style="color: #000000;">(</span><span style="color: #000000;">String ns, String file</span><span style="color: #000000;">)</span>
<span style="color: #ffffff;">            </span><span style="color: #7f0055;"><strong>throws </strong></span><span style="color: #000000;">IOException </span><span style="color: #000000;">{</span>
<span style="color: #ffffff;">        </span><span style="color: #000000;">DOMResult result = </span><span style="color: #7f0055;"><strong>new </strong></span><span style="color: #000000;">DOMResult</span><span style="color: #000000;">()</span><span style="color: #000000;">;</span>
<span style="color: #ffffff;">        </span><span style="color: #000000;">result.setSystemId</span><span style="color: #000000;">(</span><span style="color: #000000;">file</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #ffffff;">        </span><span style="color: #000000;">results.add</span><span style="color: #000000;">(</span><span style="color: #000000;">result</span><span style="color: #000000;">)</span><span style="color: #000000;">;</span>
<span style="color: #ffffff;">        </span><span style="color: #7f0055;"><strong>return </strong></span><span style="color: #000000;">result;</span>
<span style="color: #ffffff;">      </span><span style="color: #000000;">}</span>
<span style="color: #ffffff;">  </span><span style="color: #000000;">})</span><span style="color: #000000;">;</span></code>

I won’t get into the details of this code, it isn’t important right now, we just want a schema. The context object is the JAXBContext object we created earlier.

The final step is writing the schema to System.out. This is a little bit more work than the XML file:


DOMResult domResult = results.get(0);
Document doc = (Document) domResult.getNode();
OutputFormat format = new OutputFormat(doc);
format.setIndenting(true);
XMLSerializer serializer = new XMLSerializer(System.out, format);
serializer.serialize(doc);

We have to cast node from the result to a Document to let the serializer understand it. The setIndenting(true) not only indents, but also puts every tag on a new line.

The result:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="movie" type="movie"/>
  <xs:complexType name="movie">
    <xs:sequence>
      <xs:element minOccurs="0" name="genre" type="xs:string"/>
      <xs:element minOccurs="0" name="title" type="xs:string"/>
      <xs:element minOccurs="0" name="year" type="xs:int"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

Advanced

This schema isn’t right I hear you think. A movie needs at least a title, so minOccurs should not appear.

 

<code><span style="color: #646464;">@XmlElement</span><span style="color: #000000;">(</span><span style="color: #000000;">required = </span><span style="color: #7f0055;"><strong>true</strong></span><span style="color: #000000;">)</span>
<span style="color: #7f0055;"><strong>public </strong></span><span style="color: #000000;">String getTitle</span><span style="color: #000000;">() {</span>
<span style="color: #ffffff;">  </span><span style="color: #7f0055;"><strong>return </strong></span><span style="color: #000000;">title;</span>
<span style="color: #000000;">}</span></code>

Remember to put the annotation on the getter and not on the field, otherwise you’ll get the following error:

com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions

Class has two properties of the same name “title”

When you run your test again you’ll notice the minOccurs=0 has dissapeared. The order of the elements has also changed, this is not wat we want. The title must be the first element and today I think year as the second element is nice.

We have to use the @XmlType annotation for this:

<code><span style="color: #646464;">@XmlRootElement</span>
<span style="color: #646464;">@XmlType</span><span style="color: #000000;">(</span><span style="color: #000000;">propOrder = </span><span style="color: #000000;">{</span><span style="color: #2a00ff;">"title"</span><span style="color: #000000;">, </span><span style="color: #2a00ff;">"year"</span><span style="color: #000000;">, </span><span style="color: #2a00ff;">"genre"</span><span style="color: #000000;">})</span>
<span style="color: #7f0055;"><strong>public class </strong></span><span style="color: #000000;">Movie </span><span style="color: #000000;">{ </span><span style="color: #000000;">... </span><span style="color: #000000;">}</span></code>

Quite straightforward, only a pity that we have to ‘hardcode’ the element names. It would’ve been nice if the schema generator took the order as we put it in the class (but of course there is no order in the class, it’s just what we see). The good thing however is that you’ll get an error on the JAXBContext.newInstance(Movie.class); when you do something wrong with the propOrder.

 

Multiple genres

Pulp Fiction is an action movie, but it’s also crime and drama. What we usually do in Java is create a Set of genres. Unfortunately it doen’st work when you replace the String genre with Set genres, you need to put an annotation on a getter and you can’t just change the Set interface. So I created a GenreSet class:

public class GenreSet {
  private Set<String> genre;
  
  public GenreSet() {
    genre = new HashSet<String>();
  }

  public void add(String s) {
    genre.add(s);
  }

¬†¬†@XmlElement(name¬†=¬†“genre”)
  public Set<String> getGenre() {
    return genre;
  }
}

I replaced the String genre in the Movie class with GenreSet genres and when we run the test again we’ll get a set of genres:

<code><span style="color: #000000;">&lt;?xml version=</span><span style="color: #2a00ff;">"1.0" </span><span style="color: #000000;">encoding=</span><span style="color: #2a00ff;">"UTF-8" </span><span style="color: #000000;">standalone=</span><span style="color: #2a00ff;">"yes"</span><span style="color: #000000;">?&gt;</span>
<span style="color: #000000;">&lt;movie&gt;</span>
<span style="color: #ffffff;">  </span><span style="color: #000000;">&lt;title&gt;Pulp Fiction&lt;/title&gt;</span>
<span style="color: #ffffff;">  </span><span style="color: #000000;">&lt;year&gt;</span><span style="color: #990000;">1994</span><span style="color: #000000;">&lt;/year&gt;</span>
<span style="color: #ffffff;">  </span><span style="color: #000000;">&lt;genres&gt;</span>
<span style="color: #ffffff;">    </span><span style="color: #000000;">&lt;genre&gt;Drama&lt;/genre&gt;</span>
<span style="color: #ffffff;">    </span><span style="color: #000000;">&lt;genre&gt;Crime&lt;/genre&gt;</span>
<span style="color: #ffffff;">    </span><span style="color: #000000;">&lt;genre&gt;Action&lt;/genre&gt;</span>
<span style="color: #ffffff;">  </span><span style="color: #000000;">&lt;/genres&gt;</span>
<span style="color: #000000;">&lt;/movie&gt;</span></code>

Omit the year

Suppose we don’t want to show the year in XML, but we do want it to be in the Java object. We can use the @XmlTransient annotation to get this behaviour.

 

Year as attribute

It’s also possible to use a field as an attribute, use the @XmlAttribute for this.

 

Conclusion

What started out as an article to show the XmlType in JDBC turned out to be an article about XML only. After the @XmlAttribute I thought it was time to stop. Every time I started exploring a new feature I found 2 new other cool features. The java.xml.bind package is quite extensive with many possibilities.

A good thing is that everything is in the JDK, when you had a library problem in the past it probably was due to an XML libary.

 

Sources

JAXBContext javadoc

Marshaller javadoc

http://forum.java.sun.com/thread.jspa?threadID=633093&messageID=3666378

Xfire source for SchemaOutputResolver

unofficial JAXB Guide

 

Share this on .. Tweet about this on Twitter0Share on LinkedIn0Share on Facebook0Share on Google+0Email this to someoneShare on TumblrBuffer this page

About Author

Oracle Consultant at AMIS

4 Comments

  1. Thanks for this, just what I was looking for to generate a schema from my model.

    The advantage of SchemaOutputResolver being a class rather than an interface is if they want to add another method to it.√ā¬† If it is an interface adding a method breaks all existing code and stops it from compiling.√ā¬† As√ā¬†a class this isn’t necessarily the case if they SchemaOutputResolve could provide a default implementation of the method and subclassers would keep working fine.