March 2, 2008

Practical JAXB 2.0

Some days ago one of my former co-workers asked me about JAXB 2.0 as he remembered that I did some projects using JAXB while we were still at the same company. He is a real "do it yourself"-guy (at least that's my impression) and didn't put much trust in JAXB's Class Generator, XJC. He was concerned whether it really generates mappings which do exactly what he wants. And I understand why, XJC requires a well thought-through XML Schema file to work exactly the way you want it to and by exactly I mean: "somewhat close to what you want it to" :)

XJC, naturally, can't perform wonders and my impression is that many developers (in enough cases I can count myself to "those" developers) use following procedure to generate an XSD and ultimately the bindings:

1.) Create an XML-file.
2.) Use a tool of choice to generate an XSD.
3.) Run XJC

Naturally, the XML file is rarely a realistic representation of what you will get in the real world. You might just use one element by the name of product even though you may have an arbitrary number of products, you may have used used one string constant throughout your XML-file, and so on... Every XSD-generator works differently and some will use that one string as a constant, another one will say that there might just be exactly one of the previously mentoined elements, et cetara.

So, you have a sub-optimal XML, an even worse XSD and eventually run XJC. As I said, it can't perform wonders and thus you will suffer the consequences of not putting enough thought into describing your file.

In a perfect world you have a good XSD and XJC will do a very good job at generating your Java code, however, the code might still not be the way you want it to be. The two most common questions I've encountered (and which was also the case with my co-worker) are luckily quite simple to answer.

Firstly, how do I handle wrapper elements?

Say you have following XML:

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <version>5</version>
  <name>IT Books</name>
  <products>
    <product>
      <name>Learning Java</name>
      <isbn>123-123456789</isbn>
    </product>
    <product>
      <name>Learning JAXB</name>
      <isbn>234-234567890</isbn>
    </product>
  </products>
</catalog>

In this case products is the wrapper for a number of product elements. Instead of mapping an own Products class you almost certainly want to have a List of Product classes.

To accomplish this just use the @XmlWrapperElement annotation in your Catalog class as such (it's a simplification so don't complain about the looks of the class):

class Catalog {
  @XmlElement(name = "version")
  int version;
  @XmlElement(name = "name")
  String name;
  @XmlWrapperElement(name = "products")
  List<Product> products;
}

Simple, isn't it?

Secondly, how do I map enumerations?

Suppose you have following XML:

<?xml version="1.0" encoding="UTF-8"?>
<cars>
  <car>
    <brand>Mercedes</brand>
    <price>50000</price>
  </car>
  <car>
    <brand>Audi</brand>
    <price>40000</price>
  </car>
  <car>
    <brand>Volvo</brand>
    <price>30000</price>
  </car>
  ...
</cars>


In this case we use brand as our enumeration, we just have three values which are used throughout the entire XML file, namely Mercedes, Volvo and Audi.

To accomplish this we do following (Cars class not included) files:

@XmlEnum(String.class)
enum Brand {
  @XmlEnumValue("Mercedes")
  MERCEDES,
  @XmlEnumValue("Audi")
  AUDI,
  @XmlEnumValue("Volvo")
  VOLVO
}

class Car {
  @XmlElement(name = "brand")
  Brand brand;
  @XmlElement(name = "price")
  int price;
}

Equally easy :)

In the end, I simply want to stress that a good XSD is the key to success whenever you do any kind of XML-processing (no matter if it's JAXB, JAXP or any other way) in your projects. It helps you make sure the XML is ok (you would be surprised how often customers send incorrectly formatted XML-files) and it defines a clear way for your parser (or code generator) on how to treat an XML-file.

0 comments: