Java : JAXB 2.0 Java-to-Schema with annotations
This page last changed on Sep 18, 2006 by Kees de Kooter
Intro
Part of the JEE 5 stack is the JAXB 2.0 specification aka JSR-222. It is redesigned to work with Java 5 annotations. It also added the possibility of mapping POJOs to Schema.
Setting up the environment
First of all we need a JDK 5 with JEE 5 installed.
JSR-222 is a specification to be implemented by vendors. Fortunately Sun delivers a reference implementation. These are the current (September 2006) releases:
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.0EA3</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.0.2</version>
</dependency>
Mapping to namespaces
To identify to JAXB which classes it should examine in every package a file named jaxb.index
needs to be present. It contains a simple list with a classname per line. Package level annotations are placed in a file called package-info.java
The default namespace for a package can be defined with the @XmlSchema
annotation. If we want to enforce all (child) tags to contain a namespace prefix we have to set the elementFormDefault
attribute. Same goes for attributeFormDefault
.
@XmlSchema(namespace="http://www.foo.com/bar", elementFormDefault=XmlNsForm.QUALIFIED)
Because this prefix is form an XML point of view just an identifier JAXB makes up default prefixes like ns1
, ns2
. If we want to use our own prefixes we can create a subclass of NamespacePrefixMapper
that is available in the reference implementetion (Note that this is not part of the JSR-222 spec!!).
public class DefaultNamespacePrefixMapper extends NamespacePrefixMapper {
@Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
String result = suggestion;
if (namespaceUri == "http://www.foo.com/bar") {
result = "bar";
}
if (namespaceUri == "http://www.foo.com/foo") {
result = "foo";
}
return result;
}
This mapper is loaded into the marshaller like this (see below for more about the marshaller).
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper",
new DefaultNamespacePrefixMapper());
Mapping elements
Class level
@XmlRootElement
indicates that this class can be the root in an xml fragment
@XmlType(namespace="http://www.foo.com/bar", propOrder={"one", "two"
} defines the default namespace for this class and the order in which the fields will be marshalled.
Field level
@XmlTransient
makes JAXB skip this property
Custom adapters
The default marshallers of JAXB try to map the standard types to their XML counterpart. If you want to do marshal to a custom format you have to define an adapter at either the class of the package level.
@XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class)
Here is an example of a special date format:
public class DateAdapter extends XmlAdapter<String,Date> {
public static String DATE_PATTERN = "yyyyMMddHHmmssSSS";
@Override
public String marshal(Date date) throws Exception {
String result = null;
if (date != null) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
STUF_DATE_PATTERN);
result = simpleDateFormat.format(date);
}
return result;
}
@Override
public Date unmarshal(String string) throws Exception {
Date result = null;
if (!string.equals("")) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(
DATE_PATTERN);
ParsePosition pp = new ParsePosition(0);
result = dateFormatter.parse(string, pp);
}
return result;
}
}
Marshalling to XML
The actual marshalling is performed by - surprise - the Marshaller
.
public void marshal(Object object) {
try {
// Create context instance for a : separated list of packages
JAXBContext jaxbContext = JAXBContext.newInstance(
"com.foo.bar:com.foo.foo");
Marshaller marshaller = jaxbContext.createMarshaller();
// Pretty print the output for debugging purposes
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE);
// NOTE: only works with the JAXB Reference Implementation of Sun
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper",
new DefaultNamespacePrefixMapper());
marshaller.marshal(object, System.out);
} catch (JAXBException e) {
log.error(e);
}
}
Resources
- The JEE 5 tutorial http://java.sun.com/javaee/5/docs/tutorial/doc/.
- The forum on java.net http://forums.java.net/jive/forum.jspa?forumID=46.