16 January 2009

JAXB to map Java objects to XML

JAXB is a java object to XML mapping framework. It makes it rather easy to convert Java objects to and from XML. I've previously used Java DOM trees to create and write XML or XPath expressions when only reading XML.

There are two approaches in to code JABX:
  1. Start with Java objects
  2. Start with XML definition (typically an XML schema)
If you would like to go for the second approach you create a XML Schema file mySchema.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.lesc.se/blog/test/mySchemaV1.0"
xmlns:tns="http://www.lesc.se/blog/test/mySchemaV1.0"
elementFormDefault="qualified">

<element name="persons">
<complexType>
<sequence>
<element name="person" maxOccurs="unbounded" minOccurs="0">
<complexType>
<sequence>
<element name="name" type="string" />
<element name="age" type="int" />
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>

Then run this the xjc command (it is included in the JDK installation in the bin-directory) to generate Java classes:
xjc mySchema.xsd
I like to start with the Java objects since I'm a programmer and like to have control over the Java code. To do this create the Java objects and annotate the class. For example like this:
package se.lesc.blog.test.myschemav1;

import java.util.*;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement(name = "persons")
public class Persons {

@XmlElement(name = "person")
protected List<Persons.Person> persons = new LinkedList<Persons.Person>();

public List<Persons.Person> getPersons() {
return persons;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
public static class Person {

protected String name;
protected int age;

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int value) {
this.age = value;
}
}
}

You then need to make JAXB aware of the two classes you've created in special file jaxb.index:
Persons
Persons$Person
You also need a package annotation (package-info.java):
@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.lesc.se/blog/test/mySchemaV1.0", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package se.lesc.blog.test.myschemav1;
Then it is easy to produce XML from this:
//Set up your model
Persons persons = new Persons();
Person someOne = new Person();
someOne.setName("A name");
someOne.setAge(40);
persons.getPersons().add(someOne);

JAXBContext jaxbContext = JAXBContext.newInstance("se.lesc.blog.test.myschemav1");
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(persons, System.out);
Or you can read from XML back into java objects:
String xml = "<?xml.... your xml here";
StringReader xmlReader = new StringReader(xml);
JAXBContext jaxbContext = JAXBContext.newInstance("se.lesc.blog.test.myschemav");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Persons persons = (Persons) unmarshaller.unmarshal(xml);

3 comments:

  1. Actually I have a bit of a problem understanding what you mean with this example. It seems like you create the Persons.java by yourself since it's a bit different from the one xjc creates with your example schema. What do you gain by doing it yourself? And why a not an add method directly on Persons, i.e. persons.add(person) instead of persons.getPersons().add(person)? In the first case you will hide the implementation details which you don't do in the last case.

    Another question, do you really want to tie your code so hard to XML (which it seems you do by this approach). And what are your options if you want to change the behaviour of a class a little bit? Can you change the behaviour with different XML attributes for instance?

    ReplyDelete
  2. I created the Persons.java manually to have a bit more control over how the class looked like. If you want more control of how xjc generates classes there are a lot of meta-data JAXB specific tags that can be added to the schema. But since it is common to share the schema with an external part I prefer to leave such tags out of the schema. I would say it is a personal taste if method (1) or (2) should be used.

    The add() or getPersons() is a case to case scenario to where each approach is best. The advantage of getPersons() is you have access to the List.size(). But of course it also has the disadvantage of reviling the implementation.

    JAXB annotation is quite flexible if the behaviour of the class is to be changed. In my example I used field-annotation. The use of method annotation makes the XML untied to the attributes of the class. Change to XmlAccessType.PROPERTY to enable auto-mapping of the get and set methods. Annotate a custom method if it does not follow the get/set convention.

    ReplyDelete
  3. Bra jobbat Lennart:) det här exemplet var klockrent och hjälpte mig en hel del. Smidigt sätta att skapa en XML fil baserad på java-objekt.

    Tack, Mvh Jens Lundeqvist

    ReplyDelete