Advertisement

JAXB

HTML clipboard

JAXB stands for Java architecture for XML binding. Java developers in the past has been used to the XML parsers of SAX and DOM type. With JAXB, the developers have one more tool for handling XML in a robust way. JAXB is a standards specification which supports type mapping and helps in mapping Java to XML and vice versa. JAXB is a binding tool between XML and Java and provides a higher level of abstraction to work with. The reference implementation of JAXB can be seen at https://jaxb.dev.java.net. JAXB is not a parser in itself but uses one of the paresers to provide the implementation. The most popular parser being used at the moment is StAX parser.

A good way of working with JAXB is to think of Java classes representing the XML instances. The scalar data types are mapped as Java data types and the complex data types are mapped to object references. Developers who are aware of ORM framework like Hibernate or EJB3.0 entity beans can draw a parallel. JAXB does the role of mapping the XML world to Java world as OR tools do it for relational and Java world. Before proceeding further, please download JAXB reference implementation from https://jaxb.dev.java.net/. As of writing this article, the latest stable version is 2.1.12. Follow the instruction in the site to extract the artifacts.

JAXB comes with basic set of tools which help in this mapping. The tools are available in the bin directory of the above download.

* xjc - compiler to generate the Java classes from the given XML schema definition.
* schemagen - Generator to generate the XML schema definition for the given Java classes. 

Generating Java classes from XML

Let's generate the Java classes using xjc compiler. xjc tool is lying in the bin folder of jaxb download as mentioned above. We will use the following XML schema for generating the mapped Java classes. 

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<xsd:schema targetNamespace="http://www.crayom.com/om"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:om="http://www.crayom.com/om"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/XMLSchema-instance
http://www.w3.org/2001/XMLSchema-instance.xsd
http://www.w3.org/2001/XMLSchema
http://www.w3.org/2001/XMLSchema.xsd">
<xsd:element name="user" type="om:UserType" />

<xsd:complexType name="UserType">
<xsd:sequence>
<xsd:element name="homeAddress" type="om:Address" />
<xsd:element name="officeAddress" type="om:Address" />
<xsd:element name="comment" type="string" />
<xsd:element name="productBought" type="om:Products" />
</xsd:sequence>
<xsd:attribute name="startDate" type="xsd:date" />
</xsd:complexType>

<xsd:complexType name="Address">
<xsd:sequence>
<xsd:element name="houseNo" type="xsd:string" />
<xsd:element name="society" type="xsd:string" />
<xsd:element name="locality" type="xsd:string" />
<xsd:element name="city" type="xsd:string" />
<xsd:element name="pin" type="xsd:decimal" />
</xsd:sequence>
<xsd:attribute name="country" default="India">
</xsd:attribute>
</xsd:complexType>

<xsd:complexType name="Products">
<xsd:sequence>
<xsd:element name="product" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="quantity" type="xsd:integer" />
<xsd:element name="price" type="xsd:decimal" />
<xsd:element name="purchaseDate" type="xsd:date" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="productNo" type="string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>


Run the compiler as per the following command.
xjc -d <folder where the classes to be generated> <location of xsd file>
It will generate classes using the package structure as reflected by the target namespace of the xsd file. Once the classes are generated, you will see the generated java classes having annotations. Let's look one of the generated classe, 

UserType.java class
package com.crayom.om;
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.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.datatype.XMLGregorianCalendar;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "UserType", propOrder = {
"homeAddress",
"officeAddress",
"comment",
"productBought"
})

public class UserType {
@XmlElement(required = true)
protected Address homeAddress;
@XmlElement(required = true)
protected Address officeAddress;
@XmlElement(required = true)
protected String comment;
@XmlElement(required = true)

protected Products productBought;

@XmlAttribute
@XmlSchemaType(name = "date")

protected XMLGregorianCalendar startDate;

//Getter and setters not shown here

The java class has annotations which help in mapping the data in XML to attributes in Java class. Now any XML file conforming to the above XSD can be read using JAXB api. For reading the XML document, make aJava project and have the generated classes in it. Also put the following jars from JAXB download

· activation.jar 
· jaxb-api.jar 
· jaxb-impl.jar 
· jaxb-xjc.jar 

Generate a XML instance document conforming to the XSD. To read the XML document, we can use JAXB api's as shown below. Please change the file location appropriately. 

//Get the JAXB Context
JAXBContext jc = JAXBContext.newInstance("com.crayom.om");
Unmarshaller unmarshaller = jc.createUnmarshaller();

//Enable the validation handler
unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());

//Get the root and do processing in OO way
JAXBElement<UserType> userType =
(JAXBElement<UserType>)unmarshaller.unmarshal(new File("src/user_jaxb.xml"));
UserType user = (UserType)userType.getValue();
System.out.println(user.getProductBought().getProduct().size());
JAXB has api's which helps in generating the XML documents back.
//Generating the XML file using the generated code
//At the moment we will use the generated user object.
ObjectFactory of = new ObjectFactory();
JAXBElement jce = of.createUser(user);
JAXBContext jcc = JAXBContext.newInstance("com.crayom.om");
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal( jce, System.out );


The above code will generate the XML in the console.

Validating the XML Document
JAXB supports validation. It can do validation, before the XML is converted into Java objects. For validating the XML document, register the schema with the unmarsheller

//Setting the Validation
Schema schema;
SchemaFactory schemaFactory = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
schema = schemaFactory.newSchema(new File("src/user_jaxb.xsd"));
unmarshaller.setSchema(schema);

Another simple way is to register a EventHandler. A default EventHandler is also provided.

//Enable the validation handler
unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());


For customizing the validation messages a ValidationEventcollector is registered with the unmarshaller. The parsing fails at the moment the first exception is encountered. The code below might look that all the exceptions are collected at once but in reality the parsing stops the moment the first exception is encountered

//If the message regarding validation to be customized
ValidationEventCollector validationCollector= new ValidationEventCollector();
unmarshaller.setEventHandler( validationCollector );
The message can be customized by iterating on the ValidationEventCollector
for( ValidationEvent event: validationCollector.getEvents() ){
String msg = event.getMessage();
ValidationEventLocator locator = event.getLocator();
int line = locator.getLineNumber();
int column = locator.getColumnNumber();
System.out.println("Error at line " + line + " column "+ column );
}


Customizing Generated Java Code

JAXB xjc compiler generates the code when it is passed the schema definition. JAXB provides facility to customize the following aspects of generated code.

* Package names 
* class names 
* Changing enum constants 
* Associating Java types to XML built-in types 
* Documentation 

Customization can be added inline to the schema or can be provided in a separate file to the compiler. For example, to customize the class name, the extra XML fragments can be inserted into the XML schema definition. Let's change the name of our Address class

<xsd:complexType name="class">
<xsd:annotation>
<xsd:appinfo>
<jaxb:class name="Address"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="houseNo" type="xsd:string" />
<xsd:element name="society" type="xsd:string" />
<xsd:element name="locality" type="xsd:string" />
<xsd:element name="city" type="xsd:string" />
<xsd:element name="pin" type="xsd:decimal" />
</xsd:sequence>
<xsd:attribute name="country" default="India">
</xsd:attribute>
</xsd:complexType>

Now the class name will be generated as per the jaxb:class name.

Generating XML schema from Java


JAXB also supports generating of schema from the Java code. schemagen tool helps in this. Let's write a SimpleUser and SimpleAddress class with annotation and than we can generate the schema for it.

SimpleUser.java
@XmlRootElement(name="simpleUser")

public class SimpleUser {
protected List<SimpleAddress> addressList = new ArrayList<SimpleAddress>();
protected String comment;
protected int age;
@XmlElementWrapper(name="addresses")
@XmlElement(name="address")
public List<SimpleAddress> getAddressList() {
return addressList;
}

SimpleAddress.java
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "address",
propOrder = {"houseNo", "society", "locality", "city", "pin","phone"})
public class SimpleAddress {

@XmlElement
protected String houseNo;

@XmlElement
protected String society;

@XmlElement
protected String locality;

@XmlElement
protected String city;

@XmlElement
protected int pin;

@XmlTransient
protected String country;

Run the schmeagen tool on the above java classes as follows: schemagen.sh -d . //place where generated schema is kept
-cp ../bin/ //classpath
com.crayom.jaxb.SimpleUser //list of java files
com.crayom.jaxb.SimpleAddress
The generated schema looks like

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="simpleUser" type="simpleUser"/>
<xs:complexType name="simpleUser">
<xs:sequence>
<xs:element name="addresses" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="address" type="address" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="comment" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="age" type="xs:int" use="required"/>
</xs:complexType>

<xs:complexType name="address">
<xs:sequence>
<xs:element name="houseNo" type="xs:string" minOccurs="0"/>
<xs:element name="society" type="xs:string" minOccurs="0"/>
<xs:element name="locality" type="xs:string" minOccurs="0"/>
<xs:element name="city" type="xs:string" minOccurs="0"/>
<xs:element name="pin" type="xs:int"/>
<xs:element name="phone" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>

Please note how the list is handled in teh generated code.

Conclusion
JAXB is a much robust way of handling XML documents. However be careful about the limitations of JAXB in terms of the kind of mappings it can support. Also, at times it might be difficult to introduce the annotations in the existing classes for supporting JAXB only, as these classes may have relevance in other aspects. A better way to deal with this is to generate the mapping classes separetly for the purpose of JAXB only. Also avoid introducing custom code in generated classes as this might be a problem when the schema definition is changed. That might lead to regenerating the mapped classes and hence the custom code might get lost.

About Author
Lalit Bhatt is a consultant and trainer in Software application development using Java technologies. He can be reached at http://www.lalitbhatt.com or at lalit.bhatt@gmail.com








Added on September 3, 2010 Comment

Comments

#1

Peter Manoharan commented, on September 3, 2010 at 8:10 p.m.:

My name is Peter Manoharan and I want to connect with you so much. Good Magazine.

Please reply!
Australia

Post a comment