JAXB – A Newcomer’s Perspective, Part 1
I know what a lot of you are already thinking, so let’s get this out of the way: “JAXB? As in XML? Come on, all the cool kids are using JSON.”
The “XML vs. JSON” debate and the many arguments that contribute to it are pretty well documented; I won’t spend a lot of time rehashing them here. I believe that each format has its uses, but even if you’re in the “no XML ever” camp you still might want to read on, as the observations and techniques I discuss should be equally applicable to JSON data binding with Jackson (or similar tools).
In Part 1 I describe a simple usage pattern that pairs JAXB’s data binding capabilities with JPA. Of course the interactions between the two aren’t always so simple, so in Part 2 I’ll look at how to address a few of the complications you can expect to encounter.
The Problem
On my current project, we’re building a suite of Java applications to manage the staging of materials in a manufacturing process. We decided to build “from the outside in” to facilitate user-facing demos after any given iteration. So in the first iteration we built some of the screens with hard-coded dummy data; then with each successive iteration we added more infrastructure and logic behind the screens.
To make early demos more interactive, we decided to create a “test console” for the central app. A person typing commands at the console can simulate the behavior of the “net yet implemented” parts of the system. The cost to build the console is modest thanks to tools like Antlr 4 that make command parsing simple, and we see long-term value in using the console for testing and diagnostics.
We reached a point where the system’s behavior needed to be driven by data from another app. The “other app” that’s responsible for creating and maintaining this data hasn’t been written and won’t be for some time, so we needed a way to load sample data through the console.
Options
Essentially our task was to build (or leverage) a data loader. We settled on XML as a likely format for the file, and then rifled through the list of tools with which our team would generally be familiar.
DBUnit has data-loading capabilities (intended for setting up repeatable test conditions). It supports two different XML Schemas (“flat” and “full”), each of which is clearly table-oriented. It also provides for substitution variables, so we could build template files and allow the console input to set final values.
I harbor some reservations about using a unit testing tool in this way, but of the arrows in the team’s quiver it could be the closest fit. For better or worse, my first attempt to apply it was not successful (turns out I was looking at the wrong part of the DBUnit API) which got me thinking a little further outside the box.
We already had a way – namely Hibernate – to push data into our database; so when I phrased the problem in terms of “how to create entity instances from XML documents,” JAXB emerged as an obvious contender. I was pleased to discover that Java ships with a JAXB implementation, so I set to work trying it out.
A Newcomer’s Perspective
Never having used JAXB, I started with a little research. Much of the material I found dealt with generating Java classes from an XML schema. This isn’t surprising – it’s a big part of what the tool can do – but in my case, I wanted to bind data to my existing Hibernate-mapped domain classes. And that leads to something that may be a bit more surprising: some of the most comprehensive tutorials I found didn’t seem to anticipate this usage. I think this is a good demonstration of the way that your starting assumptions about a tool can shape how you think about it and how you use it.
If you start by comparing JAXB with DOM, as several online resources do, then it may be natural to think of the output of an unmarshalling operation as a document tree that needs to be traversed and processed, perhaps copying relevant data to a parallel hierarchy of domain objects. The traversal and processing may be easier (at least conceptually) than it would with a DOM tree, but as a tradeoff you have to keep the two class hierarchies straight, which calls for careful naming conventions.
There are no doubt use cases where that is exactly what is necessary, but the tool is not limited to only that approach. If you instead start by comparing JAXB with Hibernate – as a means of loading data from an external source into your domain objects – then it is natural to ask “why can’t I use one set of domain objects for both?” At least some of the time, with a little caution, you can.
The Simple Case
In these examples I’ll use the JAXB API directly. We only need to make a few simple calls to accomplish our task, so this is reasonably straightforward. It is worth noting that Spring does offer JAXB integration as well, and especially if you use Spring throughout your app, the configuration approach it offers may be preferable.
Suppose you have an EMPLOYEE table. Every employee has a unique numeric ID and a name. If you use annotations for your ORM mapping data, you might have a domain class like this:
@Entity @Table(name=”EMPLOYEE”) public class Employee { @Id @Column(name=”EMPLOYEE_ID”) private Integer employeeId; @Column(name=”FIRST_NAME”) private String firstName; @Column(name=”LAST_NAME”) private String lastName; // … getters and setters … };
Now we want to let the user provide an Employee.xml data file. Supposing we don’t have a specific XML Schema with which we need to comply, we might as well see what JAXB’s default handling of the class would be. So, we’ll start with the minimal steps to “marshal” an Employee instance into an XML document. If we’re happy with how the resulting document looks, we’ll swap in the unmarshalling code; if not, we can look into customizing the mapping.
First we need a JAXBContext instance configured to work with our domain class(es).
JAXBContext jaxb = JAXBContext.newInstance(Employee.class);
As an aside, instead of passing the class object(s) to newInstance(), we could pass in the name(s) of the package(s) containing the classes, provided each package contains either a jaxb.index file that lists the classes to use or an ObjectFactory class with methods for creating instances of the domain classes (and/or JAXBElements that wrap them). This approach might be preferable if you need XML mappings for a large number of unrelated domain classes.
The JAXBContext has methods for creating marshallers (which create XML documents to represent objects) and unmarshallers (which instantiate objects and initialize them from the data in XML documents). We can check out the default mapping for our Employee class like this:
Employee employee = new Employee(); employee.setEmployeeId(37); employee.setFirstName(“Dave”); employee.setLastName(“Lister”); Marshaller marshaller = jaxb.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(employee, System.out);
(The setProperty() call isn’t strictly necessary but makes the output much more human-readable.) If we try running this code, we’ll get an exception telling us that we haven’t identified a root element. To fix this we add the @XmlRootElement annotation to our Employee class.
@XmlRootElement @Entity @Table(name=”EMPLOYEE”) public class Employee { @Id @Column(name=”EMPLOYEE_ID”) private Integer employeeId; @Column(name=”FIRST_NAME”) private String firstName; @Column(name=”LAST_NAME”) private String lastName; // … getters and setters … };
By default, the marshaller will map every public bean property (getter/setter pair) and every public field; so if our Employee class has the getters and setters you’d expect, then our output should look something like this:
<?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?> <employee> <employeeId>37</employeeId> <firstName>Dave</firstName> <lastName>Lister</lastName> </employee>
Note that the elements under will be in an arbitrary order. (In my tests it’s been alphabetical.) In this case that works out nicely, but if it didn’t we could force the order using the @XmlType annotation. The unmarshaller will, by default, take the elements in any order.
JAXB is happily ignorant of the JPA annotations, and Hibernate (or whatever JPA provider you might use) will disregard the JAXB annotations, so we can now load data from XML files into our database by simply asking JAXB to unmarshal the data from the files and passing the resulting objects to the JPA provider. The unmarshalling code would look like this:
JAXBContext jaxb = JAXBContext.newInstance(Employee.class); Unmarshaller unmarshaller = jaxb.createUnmarshaller(); File xmlFile = /* … */; Employee employee = unmarshaller.unmarshal(xmlFile);
By default if an element that represents one of the bean properties is omitted from the XML, that property simply isn’t set; so for example if our JPA mapping includes automatic generation of employeeId, then the <employee> element need only contain <firstName> and <lastName>.
The Good…
In theory, that’s about it. (Extra credit if you know the difference between theory and practice.) A couple annotations and maybe a dozen lines of code are enough to get you started. As an added benefit, you can see the relationships between all of your data’s representations (XML, database, and Java object) in a single annotated .java file.
The Not So Good…
The above example is simple and may cover a fair number of basic use cases; but most real data models include things like one-to-many relationships and composite keys, which add wrinkles you may or may not foresee. In Part 2 (slated for August 25, 2014) I will address some of the complications I have encountered and discuss reasonably simple options for addressing each of them.
Reference: | JAXB – A Newcomer’s Perspective, Part 1 from our JCG partner Mark Adelsberger at the Keyhole Software blog. |