JAXB Tutorial

By Maurizio Farina | Posted on August 2017

Java Architecture for XML Binding (JAXB) is a framework to map Java classes to XML and convert Java object to XML document and viceersa.

JAXB is supported by other Java-related technologies, such as JAX-RS (a Java RESTful API). JAX-RS can serve and receive JAXB objects natively.

Generally many developers prefers to annotate the Java code using JAXB annotations and marshal/unmarshal XML document.

This approach is very fast and easy but sometime is not enough, for example when XML schema is many complex or when it change continuosly.

JAXB, beyond annotations, includes more than one components to manage all development scenarios:

Architectural Overview

  • How to map existing Java classes to a XML schema? JAXB annotations, used direclty in Java code, are used by a Schema generator component
  • How to generate Java classes from XML Schema? a Schema compiler component allow to bind a XML schema to a new set of Java classes. The component can be integrated in code building pipeline, for example using a Maven plug-ins.

Java8 provides by default a JAXB Framework implementation including unmarshalling, marshalling, and validation capabilities in java.xml.bind package. So, no dependencies have to be included in Java project)

JAXB Annotations

The easy way to work with JAXB is using annotation; just annotate the java code with JAXB annotations to map java class and attributes to XML document.

Note: JAXB treat properties (get/set pairs), public fields (instance variables), and annotated non-public fields as mapped. Annotating the field we receive a duplicate mapped property exception. To avoid simply use: @XmlAccessorType(XmlAccessType.FIELD) on the class.

A simple list of most used JAX annotations:

Annotations Brief Description
@XmlSchema Maps a Java package to XML namespace
@XmlRootElement Define the root element for the XML. The name of the root XML element is the class name.
@XmlElement Used for fields to inlude in XML as element
@XmlAttribute Used for fields to inlude in XML as attribute
@XmlType Maps a class or an enum type to a XML Schema type
@XmlTransient Used for fields that will be not included in XML
@XmlAccessorType Manage the serialization of fields and properties. For example: @XmlAccessorType (value = AccessType.PUBLIC_MEMBER )
@XmlJavaTypeAdapter Allows to specify a Java class that implements the @XmlAdapter annotation for custom marshalling

An example from ListFeeds project used to manage a list of feeds. The XML is something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?xml version="1.0" encoding="UTF-8"?>
<extractors>
    <extractor active="true" name="Napoli" >
        <feeds>
            <feed type="Rss" source="ilcorrieredellasera" url="http://www.corriere.it/rss/homepage_napoli.xml" />
            <feed type="Rss" source="larepubblica" url="http://napoli.repubblica.it/rss/rss2.0.xml" />
            ....            
        </feeds>
        ....
</extractors>

The Java classes: Extractors, Extractor, Feed

Extractors.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.listfeeds.settings;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/* 
@XmlRootElement, @XmlElement and @XmlAttribute allow to specify the name used to map XML
*/
@XmlRootElement(name="extractors")
@XmlAccessorType(XmlAccessType.FIELD)
public class Extractors {

    @XmlElement(name="extractor")
    List<Extractor> extractor = new ArrayList<Extractor>();

    public List<Extractor> getExtractors() {
        return extractor;
    }

    public void setExtractors(List<Extractor> extractors) {
        this.extractor = extractors;
    };
}

Extractor.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.listfeeds.settings;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlType;

/*
@XmlAccessorType:  specify to manage class Extrator as "FIELD". 
In this case is possible to annotate directly class attributes.
@XmlType: is used to decide the order to use during marshal phase
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = {
        "active",
        "name", 
        "feed"})
public class Extractor {

    @XmlAttribute
    String name;

    @XmlAttribute
    Boolean active;

    @XmlElementWrapper(name="feeds")
    @XmlElement(name="feed")
    List<Feed> feed = new ArrayList<Feed>();

    public Extractor() {;}

    public Extractor(String name, Boolean active) {
        this.setActive(active);
        this.setName(name);
    }

    public String getName() {
        return name;
    }


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

    public Boolean getActive() {
        return active;
    }

    public void setActive(Boolean active) {
        this.active = active;
    }


    public List<Feed> getFeeds() {
        return feed;
    }

    public void setFeeds(List<Feed> feeds) {
        this.feed = feeds;
    }

}

Feed.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.listfeeds.settings;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "FeedType", propOrder = {
        "category",
        "source",
        "type",
        "url"})
public class Feed {

    @XmlAttribute(required = true)
    protected String url;

    @XmlAttribute(required = true)
    protected String source;

    @XmlAttribute(required = false)
    protected String category;

    @XmlAttribute(required = false)
    protected String type;

    public Feed() {;}

    public Feed(String source, String category, String type, String url) {
        this.setSource(source);
        this.setCategory(category);
        this.setType(type);
        this.setUrl(url);
    }

    public String getSource() {
        return source;
    }


    public void setSource(String source) {
        this.source = source;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String defaultCategory) {
        this.category = defaultCategory;
    }

    public String getType() {
        return type;
    }


    public void setType(String type) {
        this.type = type;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

JAXB and namespaces

Generally the XML document are namespace qualified. JAXB allows to specify namespace for the major annoation such as XmlRootElement, XmlElement and so on.

For our example we prefer to use namespace annotation in only one point so we create package-info.java file for the Java package com.listfeeds.settings.

In Java the purpose of file package-info.java is provide package level documentation and package level annotations.

This is package-info.java file:

1
2
3
4
5
6
@XmlSchema(namespace="http://www.listfeeds.com/1.0/schema", 
    elementFormDefault = XmlNsForm.QUALIFIED) 
package com.listfeeds.settings;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNsForm;

Adding namespace to our classes we need to modify our XML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?xml version="1.0" encoding="UTF-8"?>
<extractors xmlns="http://www.listfeeds.com/1.0/schema">
    <extractor active="true" name="Napoli" >
        <feeds>
            <feed type="Rss" source="ilcorrieredellasera" url="http://www.corriere.it/rss/homepage_napoli.xml" />
            <feed type="Rss" source="larepubblica" url="http://napoli.repubblica.it/rss/rss2.0.xml" />
            ....            
        </feeds>
        ....
</extractors>

Is possible to override the package level namespace information simply using namespace for annotation like as @XmlType, @XmlElement:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@XmlType(namespace="http://www.mkjava.org/your_type")
public class your_class {
    ....
}

or

@XmlAccessorType(XmlAccessType.FIELD)
public class your_class {

    @XmlElement(namespace="http://www.mkjava.org/your_type")
    Another_your_class feed;

    ....
}

How to Marshal and Unmarshal XML

The following example, as JUnit tests, use JAXB to marshal and unmarshal XML strings using the standard javax.xml.bind interfaces.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.listfeeds.settings;

import static org.junit.Assert.*;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.junit.Test;

public class TestExtractors {

    @Test
    public void Unmarshal() throws JAXBException, IOException {

        InputStream in = this.getClass().getResourceAsStream("/extractors.xml");

        JAXBContext jaxbContext = JAXBContext.newInstance(Extractors.class);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

        Extractors extractors = (Extractors) jaxbUnmarshaller.unmarshal(in);

        assertNotNull(extractors.getExtractors());
        assertEquals(extractors.getExtractors().size(),10);
        assertNotNull(extractors.getExtractors().get(0).getFeeds());
        assertEquals(extractors.getExtractors().get(0).getFeeds().size(),7);
        assertEquals(extractors.getExtractors().get(0).getFeeds().get(0).getUrl(),"http://www.corriere.it/rss/homepage_napoli.xml");

        in.close();
    }

    @Test
    public void Marshal() throws JAXBException, IOException {

        Feed feed = new Feed("sportmagazine", "sport","RSS","www.sportmagazine.com");
        Extractor extractor = new Extractor("Napoli", true);
        extractor.getFeeds().add(feed);

        Extractors extractors = new Extractors();
        extractors.getExtractors().add(extractor);

        JAXBContext jaxbContext = JAXBContext.newInstance(Extractors.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);

        //marshal to a file
        jaxbMarshaller.marshal(extractors, new File("c://temp//extractors.xml"));

        //marshal to stream such as System.out
        jaxbMarshaller.marshal(extractors, System.out);

        //marshal to a string
        java.io.StringWriter sw = new StringWriter();
        jaxbMarshaller.marshal(extractors, sw);
        assertEquals(sw.toString(),"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><extractors xmlns=\"http://www.listfeeds.com/1.0/schema\"><extractor active=\"true\" name=\"Napoli\"><feeds><feed category=\"sport\" source=\"sportmagazine\" type=\"RSS\" url=\"www.sportmagazine.com\"/></feeds></extractor></extractors>");
    }
}

Default data type bindings

The table below extracts from wikipedia lists the mappings of XML Schema (XSD) data types to Java data types in JAXB

XML Schema Type Java Data Type
xsd:string java.lang.String
xsd:integer java.math.BigInteger
xsd:positiveInteger java.math.BigInteger
xsd:int int
xsd:long long
xsd:short short
xsd:decimal java.math.BigDecimal
xsd:float float
xsd:double double
xsd:boolean boolean
xsd:byte byte
xsd:QName javax.xml.namespace.QName
xsd:dateTime javax.xml.datatype.XMLGregorianCalendar
xsd:base64Binary byte[]
xsd:hexBinary byte[]
xsd:unsignedInt long
xsd:unsignedShort int
xsd:unsignedByte short
xsd:unsignedLong java.math.BigDecimal
xsd:time javax.xml.datatype.XMLGregorianCalendar
xsd:date javax.xml.datatype.XMLGregorianCalendar
xsd:g javax.xml.datatype.XMLGregorianCalendar
xsd:duration javax.xml.datatype.Duration
xsd:NOTATION javax.xml.namespace.QName

XJC and Schemagen

The JDK bin folder includes the JAXB binding compiler tool xjc, to generate Java classes starting from a XML schema, and Schema generator tool schemagen to generate a XML Schema starting from Java classes annotated using JAXB annotations.

The tools are executale from:

  • directly from console as commands
  • Ant Task
  • Maven plug-in
  • Eclipse plug-in

In our case we prefer to use those tools from build automation tool, such as Maven. Our example use JAXB2 Maven Plugin version 2.2.

The plug-in supports fews goals we use the followings:

Goal Description
jaxb2:schemagen Create XSD files starting from Java classes. Is possible to specify parameters for example clearOutputDir to remove all files from the output directory
jaxb2:xjc Create JAXB annotated classes starting from XSD

Add this to your Maven pom file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <version>2.2</version>
            <executions>
                <execution>
                    <id>schemagen</id>
                    <goals>
                        <goal>schemagen</goal>
                    </goals>
                </execution>
                </executions>
                <configuration>
                    <outputDirectory>generated/xsds</outputDirectory>
                    <sources>
                        <source>src/main/java/com/listfeeds/settings</source>
                    </sources>
                    <transformSchemas>
                        <transformSchema>
                            <uri>http://www.listfeeds.com/1.0/schema</uri>
                            <toFile>listfeeds.xsd</toFile>
                        </transformSchema>
                    </transformSchemas>
                </configuration>
        </plugin>
    </plugins>
</build>

Parameters used by the plug-in in our example:

  • source: In our example the JAXB annotated classes are all in com.listfeeds.settings package so the source parameter is used to specify in which folder are the annotated classes
  • transforSchemas: is a post processor used to assign the name listfeeds.xsd to the XSD namespace used by our code. Is possible to transform all XSD namespace, for example by @XmlType annotation, used by our code
  • outputDirectory: where generate the XSD files

Sometime we need to integrate our code with external legacy systems for example using Web Services. We receive XSD files describing the data format. We can use the same plug-in to generate JAXB annotated classes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>2.2</version>
    <executions>
        <execution>
            <id>xjc</id>
            <goals>
                <goal>xjc</goal>
            </goals>
        </execution>

    </executions>
    <configuration>
        <outputDirectory>generated/java</outputDirectory>
        <packageName>com.listfeeds.settings</packageName>
        <sources>
        <source>generated/xsds/listfeeds.xsd</source>
    </sources>
    </configuration>
</plugin>

Parameters used by the plug-in in our example:

  • source: where to find XSD files
  • packageName: is the Java namespace to create
  • outputDirectory: where generate the Java files
Link Description
Oracle JAXB site Oracle JAXB WebSite
Oracle JAXB Tutorial An exaustive tutorial from Oracle
xjc xjc guide
schemagen schemagen guide
JAXB2 Maven Plugin JAXB2 Maven plugin to use in pom file
Wikipedia page Wikipedia page for JAXB
javax.xml.bind Java 8 javax.xml.bind package