Java Architecture for XML Binding (JAXB)

So, I was working on a little program to make it easier to configure collections of
servos making up the
robot I’m working on when I got to the point of figuring
out how I was going to persist this information. Although the sum total of
configuration is trivial at the moment I have plans to eventually include
enough structural information about the robot that a simple textual list of
properties would become unwieldy. An XML format seemed like the obvious
choice.

I wrote a simple Relax NG schema and was just about to
write yet another SAX based parser and Java
Writer
when I
had this sense that I’d been in this situation way too many times before and
that there had to be a better way. Processing an in-memory
DOM tree and then using
Java’s XML processing to read/write it didn’t sound like a whole lot of fun so
in the end I thought I’d give JAXB a go. I’d
heard about it years ago (it’s not a new Java technology) but not had an
excuse to use it before so now seemed like a good time to give it a go.

There are several tutorials and articles about JAXB out there - unfortunately
they all seem to cover different versions. JAXB 2.x seems much less hassle to
use than JAXB 1.x. Also, while the current version of JAXB is 2.1, the version
of JAXB bundled with the Java 1.6 SDK is 2.0 and the bundled version doesn’t
have the ant task. You can download the current version of JAXB from
jaxb.dev.java.net

In order to prevent examples getting too long we’ll stay with a fragment of
the complete data structure which consists of servos and a Robot which is
simply a collection of servos.

The impression I got from reading the tutorials and documentation is that you
need to start with a schema and generate Java classes from there. I’m not a
great fan of XML Schema so I decided to stick with my Relax NG schema:

`
<?xml version=”1.0” encoding=”UTF-8”?>




























`

I then used Oxygen XML‘s built-in copy of
trang to convert this to XML Schema:

<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="robot"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" ref="servo"/> </xs:sequence> <xs:attributeGroup ref="identifiable-element"/> </xs:complexType> </xs:element> <xs:attributeGroup name="identifiable-element"> <xs:attribute name="id" use="required"/> <xs:attribute name="name" use="required"/> </xs:attributeGroup> <xs:element name="servo"> <xs:complexType> <xs:attributeGroup ref="identifiable-element"/> <xs:attribute name="min_angle" default="-1.570796327" type="xs:decimal"/> <xs:attribute name="max_angle" default="1.570796327" type="xs:decimal"/> <xs:attribute name="rest_angle" default="0" type="xs:decimal"/> <xs:attribute name="min_pulse_width" default="500" type="xs:integer"/> <xs:attribute name="max_pulse_width" default="2500" type="xs:integer"/> <xs:attribute name="max_speed" default="2500" type="xs:integer"/> <xs:attribute name="desired_position" default="0" type="xs:integer"/> </xs:complexType> </xs:element> </xs:schema>

Running xjc from the command line then generated 5 Java classes that would
allow me to read/write this format. There was were just a couple of snags:
- The generated classes Robot and Servo had no behaviour. In my existing
codebase these classes needed behaviour and editing generated code is clearly
a bad idea.
- My hand written classes used primitive int and double but the
generated classes used java.lang.Integer and java.lang.Double. [1]

There is a section in the Unofficial JAXB
Guide
on adding
behaviour
to generated
classes which suggests that the solution to my first problem was to define
classes which extend the generated classes to add the behaviour and then
create a custom factory class that makes JAXB instantiate instances of the
subclasses instead of the generated classes.

The solution to the second problem seemed to be to use JAXB’s support for
customisation to force it to use primitive int and double for the attribute
values.

You can customise
how JAXB generates classes using annotations in the schema or an external
binding file. Since I was generating my schema from Relax NG and, in any case
I wanted to keep the schema clean, I elected to go for an external binding
file.

I wanted to keep convenient names for my hand-written classes and so I elected
to use customisation to both change the data types of the generated classes
instance variables and to rename the generated classes to have the suffix
“Element.” Here is my final attempt at a JAXB binding file to do this:

<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <jxb:bindings schemaLocation="robot.xsd" node="/xs:schema"> <jxb:globalBindings> <jxb:javaType name="int" xmlType="xs:integer"/> <jxb:javaType name="double" xmlType="xs:decimal" /> </jxb:globalBindings> <jxb:schemaBindings> <jxb:package name="org.emptiness.hexapod.autogen.robotmodel"> <jxb:javadoc> <![CDATA[<body> Package level documentation for generated package org.emptiness.hexapod.autogen.robotmodel.</body>]]> </jxb:javadoc> </jxb:package> <jxb:nameXmlTransform> <jxb:elementName suffix="Element"/> </jxb:nameXmlTransform> </jxb:schemaBindings> </jxb:bindings> </jxb:bindings>

Unfortunately, although this did correctly generate classes called
RobotElement and ServoElement the element attribute values were still
mapped to java.lang.Integer and java.lang.Double - some more googling
found this forum
post
and another
one
–instead-of-
class-types-td20816710.html) which indicated that JAXB was forcing the use of
classes since the attributes were optional and might therefore need to be
null. There did not seem to be any way around this and so I reluctantly
decided to leave things as they were and be grateful that Java supports
autoboxing so that I could get away without having to modify any code using my
Servo class.

I created my custom factory:

`
package org.emptiness.hexapod.core;

import javax.xml.bind.annotation.XmlRegistry;

import org.emptiness.hexapod.autogen.robotmodel.ObjectFactory;
import org.emptiness.hexapod.autogen.robotmodel.RobotElement;
import org.emptiness.hexapod.autogen.robotmodel.ServoElement;

@XmlRegistry
public class RobotObjectFactory extends ObjectFactory {
/**

  • Create an instance of {@link RobotElement }
  • */
    public RobotElement createRobotElement() {
    System.err.println(“RobotElement createRobotElement”);
    return new Robot();
    }

/**

  • Create an instance of {@link ServoElement }
  • */
    public ServoElement createServoElement() {
    System.err.println(“ServoElement createServoElement”);
    return new Servo();
    }
    }
    `

But try as I might I could not get JAXB to use to create instances of my
classes instead of the ones it had generated. By this time I was thoroughly
fed up with JAXB and ready to try something else. A little further
reading
suggested that
the way forward might be to use annotations to mark up my handwritten classes
and abandon the whole idea of getting JAXB to generate the data bearing
classes. After some messing about I had a new Servo class that looked a little
like this (for brevity I’ve removed all the constant declarations, behaviour
methods and getters/setters from the code below):

`
package org.emptiness.hexapod.core;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.emptiness.hexapod.core.jaxb.DoubleAdapter;
import org.emptiness.hexapod.core.jaxb.IntegerAdapter;
import org.emptiness.hexapod.device.ServoController;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”)
@XmlRootElement(name = “servo”)
public class Servo implements RobotComponent {

// configuration information
@XmlAttribute(required = true)
@XmlSchemaType(name = “anySimpleType”)
private String id; // identifier passed to the controller

@XmlAttribute(required = true)
@XmlSchemaType(name = “anySimpleType”)
private String name; // meaningful human name

@XmlAttribute(name = “min_angle”)
@XmlJavaTypeAdapter(DoubleAdapter.class)
@XmlSchemaType(name = “decimal”)
private Double minAngle; // (radians)

@XmlAttribute(name = “max_angle”)
@XmlJavaTypeAdapter(DoubleAdapter.class)
@XmlSchemaType(name = “decimal”)
private Double maxAngle; // (radians)

@XmlAttribute(name = “rest_angle”)
@XmlJavaTypeAdapter(DoubleAdapter.class)
@XmlSchemaType(name = “decimal”)
private Double restAngle; // (radians)

@XmlAttribute(name = “min_pulse_width”)
@XmlJavaTypeAdapter(IntegerAdapter.class)
@XmlSchemaType(name = “integer”)
private Integer minPulseWidth; // (microseconds)

@XmlAttribute(name = “max_pulse_width”)
@XmlJavaTypeAdapter(IntegerAdapter .class)
@XmlSchemaType(name = “integer”)
private Integer maxPulseWidth; // (microseconds)

@XmlAttribute(name = “max_speed”)
@XmlJavaTypeAdapter(IntegerAdapter .class)
@XmlSchemaType(name = “integer”)
private Integer maxSpeed; // (max change in pulse width per second)

// current state
@XmlTransient
private int actualPosition = UNKNOWN_POSITION;

@XmlTransient
private int desiredPosition = UNKNOWN_POSITION;

@XmlTransient
private int speed = DEFAULT_SPEED;

@XmlTransient
private ServoController controller = null;
}
`

Since I was now annotating classes which contained state that should not be
persisted I had to use @XmlTransient to prevent JAXB from attempting to
persist those instance variables.

The Robot class now looked like this (again with large chunks of code
removed):

`
package org.emptiness.hexapod.core;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”, propOrder = {
“servo”
})
@XmlRootElement(name = “robot”)
public class Robot {

protected List servo;

@XmlAttribute(required = true)
@XmlSchemaType(name = “anySimpleType”)
protected String id;
@XmlAttribute(required = true)
@XmlSchemaType(name = “anySimpleType”)
protected String name;

public Robot() {

}

// file operations
public static Robot load(File inputFile) throws Exception {
//JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
JAXBContext context = JAXBContext.newInstance(RobotObjectFactory.class);
Unmarshaller u = context.createUnmarshaller();
//u.setProperty(“com.sun.xml.bind.ObjectFactory”,new RobotObjectFactory());
Robot robot = (Robot) u.unmarshal(inputFile);
return robot;
}

public void save(File ouptutFile) throws Exception {
JAXBContext context = JAXBContext.newInstance(RobotObjectFactory.class);
Marshaller m = context.createMarshaller();
m.setProperty(“jaxb.formatted.output”, Boolean.TRUE);
OutputStream os = new FileOutputStream(ouptutFile);
m.marshal(this, os);
}

public List getServos() {
return getServo();
}

public List getServo() {
if (servo == null) {
servo = new ArrayList();
}
return this.servo;
}
}
`

Since the servo element is called “servo” JAXB expects the getter methods for
accessing the list to be called “getServo” - I, however, felt the method was
more naturally called “getServos” and this was what I used in my existing
code. I therefore created a wrapper method to allow me to continue using
“getServos” The load() and save() methods above show the small amount of code
actually required to invoke JAXB to read or write the data from/to a file.

So, I now have the ability to read and write my data to XML without having had
to write yet more code to implement a SAX interface or generate XML with
PrintWriter.println() but getting things working with JAXB took me far longer
than the “SAX + println()” approach would have done and I can’t say that I’m
100% happy with the final result. I may eventually try another approach like
Xstream and see if that gives better results
with less overall hassle.

Links
- Official JAXB home page
- Wikipedia page on JAXB
- FAQ entry on adding behaviours to JAXB generated
classes

- A practical guide to JAXB 2.0 (The
Register)

- XML annotations
javadoc

- Xstream

View the discussion
thread.