Monday 6 August 2012

JAXB Annotation - Basic Annotations, Inheritance

JAXB (Java Architecture for XML Binding) enables the mapping of Java object into XML documents back and forth. This post is an introduction, tutorial and summary of JAXB annotations. It proceeds with operational code examples to describe each feature.

The code examples are available from Github in the JAXB-JSON-XML-Marshalling directory. Most examples rely on the following piece of code to create XML files from annotated Java objects:
    public static void createXML(Object o) throws JAXBException {
        
        // Creating a Marshaller
        JAXBContext jaxbContext = JAXBContext.newInstance(o.getClass());
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 
        StringWriter result = new StringWriter();
        jaxbMarshaller.marshal(o, result);

        // Printing XML
        String xml = result.toString();
        System.out.println(xml);
        
    } 
In order to generate such XML documents, a JAXB context must be created. More details on this in part II.

@XmlRootElement

Defines the root element of the XML document. If not name is specified (like this for example: @XmlRootElement(name = "MyRootName"), the name of the element is taken from the class name.

This annotation can be used on a Class or on an enum. A Class does need a no parameter constructor or a factory method (more details in the @XmlType section).

For example:
@XmlRootElement(name="MyRootName")
public class A {

    private int a1 = 0;

    @XmlElement
    public int getA1() {
        return a1;
    }

    public void setA1(int a1) {
       this.a1 = a1;
    }

}
generates the following XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MyRootName>
   <a1>28</a1>
</MyRootName>
One can also specify a namespace name for the XML element or a local name for the XML element.

Inheritance

When a class inherits of another annotated class, the root name is taken from the inheriting class:
@XmlRootElement
public class InheritsA extends A {

    private int b1 = 0;

    @XmlElement
    public int getB1() {
        return b1;
    }

    public void setB1(int b1) {
        this.b1 = b1;
    }

}
The generated XML is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<inheritsA>
    <a1>33</a1>
    <b1>66</b1>
</inheritsA>

@XmlElement / @XmlTransient

The @XmlElement indicates which element should be included in the XML conversion. If you want to annotate fields instead of getter methods, use the @XmlAccessorType(XmlAccessType.FIELD).

If you need to exclude a field from the generated XML, use the @XmlTransient annotation.
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class FieldAnnotation {

    @XmlElement
    private int a1 = 45;

    @XmlTransient
    private long a2 = 0;

    @XmlElement(nillable=true)
    private String xxx = null;

    @XmlElement(required=true)
    private String req;

    public int getA1() {
        return a1;
    }

    public void setA1(int a1) {
        this.a1 = a1;
    }

    public long getA2() {
        return a2;
    }

    public void setA2(long a2) {
        this.a2 = a2;
    }

    public String getXxx() {
        return xxx;
    }

    public void setXxx(String xxx) {
        this.xxx = xxx;
    }

    public String getReq() {
        return req;
    }

    public void setReq(String req) {
        this.req = req;
    }

} 
Assuming the following class creation:
FieldAnnotation fa = new FieldAnnotation();
fa.setA2(28);
fa.setReq("Some value");
generates:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<fieldAnnotation>
    <a1>45</a1>
    <xxx xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
    <req>Some value</req>
</fieldAnnotation>
One can also set a default string value, or set the XML Schema element name and the target namespace of the XML element via the @XmlElement annotation.

@XmlType

This annotation allows one to specify the order of items in the generated XML document. It can also be used to specify a factory class and/or a factory method (with no arguments) to create instances of the annotated class:
@XmlRootElement
@XmlType(propOrder={"a2", "a1"},
  factoryClass=OrderFactory.class,
  factoryMethod="myConstructor")
public class Order {

    private int a1 = 0;
    private int a2 = 0;

    @XmlElement
    public int getA1() {
        return a1;
    }

    public void setA1(int a1) {
        this.a1 = a1;
    }

    @XmlElement
    public int getA2() {
        return a2;
    }

    public void setA2(int a2) {
        this.a2 = a2;
    }

}
The factory class:
public class OrderFactory {
    public static Order myConstructor() {
        return new Order();
    }
}
The generated XML:
<order>
  <a2>99</a2>
  <a1>28</a1>
</order>
One can also set the XML Schema type and the target namespace of the XML Schema type with the @XmlType annotation.

@XmlSeeAlso

This annotation allows one to refer to other classes to include in the XML generation. In other words, if a referred class is not in the JAXB context, it will be taken into account anyway and won't trigger a runtime error. This annotation can be used as a safeguard when marshalling and unmarshalling.

This is best described with an example:
@XmlRootElement
public class Document {

    private String content = "";

    @XmlElement
    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

}
@XmlRootElement
public class Image {

    private String name = "";

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String content) {
        this.name = content;
    }

}
@XmlRootElement
@XmlSeeAlso({Document.class,Image.class})
public class Folder {

    private Document document = null;
    private Image image = null;

    @XmlElement
    public Document getDocument() {
        return document;
    }

    public void setDocument(Document document) {
        this.document = document;
    }

    @XmlElement
    public Image getImage() {
        return image;
    }

    public void setImage(Image image) {
        this.image = image;
    }

}
The generated output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<folder>
    <document>
        <content>My content</content>
    </document>
    <image>
        <name>My image</name>
    </image>
</folder>

Next

JAXB to XMLJAXB to JSONJAXB Annotations Tutorial Table Of Content

5 comments:

  1. Hi,

    Nice article. One small correction when @XmlAccessorType(XmlAccessType.FIELD) is specified by default a mapping is created unless annotated with @XmlTransient. The @XmlElement annotation is only needed when you want to override the default behaviour.
    - Using JAXB's @XmlAccessorType to Configure Field or Property Access

    -Blaise

    ReplyDelete
  2. Hi,
    Thanks for this article. Now I'm wonderring how to unmarshall to the correct class.
    Does anyone knows how this is done based on this example?

    ReplyDelete
  3. nice article, very easy to understand.

    ReplyDelete
  4. Thanks, this post is useful to me.

    ReplyDelete