Friday, February 15, 2008

JAXB Overview

JAXB is used to easily construct an xml message from Java objects and
the reverse i.e to construct Java objects from XMl message.
JAXB now comes standard with the Java Web Services Development Pack
(http://java.sun.com/webservices/downloads/webservicespack.html).


In this tip we can see how to use JAXB to generate a set of Java classes
from an XML Schema document using the JAXB binding compiler.
We can also see how to serialize an object to an XML file (marshalling)
and how to do the opposite: serialize an object from an XML
file (unmarshalling)

We require the following files
jaxb-api.jar
jaxb-impl.jar
jaxb-libs.jar
jaxb-xjc.jar
relaxngDatatype.jar
xsdlib.jar


Compile XML schema to Java classes

Here is a simple schema, Person.xsd, that can be used to generate a JAXB
class.

<xs:element name="person">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="age" type="xs:int" />
<xs:element name="address" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>

The schema defines a simple element named person. The person element has a complexType
that defines three properties: name (a String), age
(an integer), and address, (another String).

To bind this to a Java interface, invoke the XML binding
compiler. To do this you run the xjc command.
Use the following command-line syntax:

xjc -p <target_package> -d <dir> schema.xsd

where <target_package> is the name of the package for the
generated files, <dir> is the directory in which the
generated files will be placed, and schema.xsd is the input XML
schema. So, for example, run the following command in the
directory that contains Birthdate.xsd:

xjc -p com.mytest.example -d . Person.xsd

In response, the binding compiler generates a set of Java source
classes, all of which need to be compiled with javac.

Here, for example, is the PersonType interface:

package com.mytest.example;

public interface PersonType {

int getAge();
void setAge(int value);

java.lang.String getName();
void setName(ava.lang.String value);

java.lang.String getAddress();
void setAddress(java.lang.String value);

}

Along with this, the schema compiler also generates some vendor-specific implementations of those
interfaces and other supporting runtime classes.

After the binding compiler generates the necessary classes,
the next step is to compile the generated classes with javac.
Then you can start working with JAXB serialization.


Alternatively we can also compile the schema through an ant script as below:

<target name="compile_jaxb" depends="delete_jaxb_tmp, set_jaxb_path">
<echo message="Doing a clean compile"/>
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask">
<classpath>
<fileset dir="${src}/WEB-INF/lib">
<include name="**/xalan.jar" />
</fileset>
<fileset dir="${src}/../lib/buildtime/jaxb1.0" >
<include name="**/*.jar" />
</fileset>
</classpath>
</taskdef>

<mkdir dir="${jaxb.tmp.src}"/>
<xjc schema="${schema.file}" binding="${bindings.file}" package="${package}" target="${jaxb.tmp.src}"/>

<mkdir dir="${jaxb.tmp.classes}"/>

<javac srcdir="${jaxb.tmp.src}"
destdir="${jaxb.tmp.classes}"
debug="on"
fork="true"
memoryInitialSize="128m"
memoryMaximumSize="192m">
<classpath>
<fileset dir="${src}/../lib/buildtime/jaxb1.0" >
<include name="**/*.jar" />
</fileset>
</classpath>
</javac>

<copy todir="${jaxb.tmp.classes}">
<fileset dir="${jaxb.tmp.src}" includes="**/*.properties, **/*.ser"/>
</copy>

<jar jarfile="${jaxb.tmp}/${jaxbJar.file}"
basedir="${jaxb.tmp.classes}"/>
</target>



<target name="delete_jaxb_tmp">
<echo message="Deleting the temp directory"/>
<delete dir="${jaxb.tmp}"/>
</target>

<target name="set_jaxb_path">
<property name="schema.root" value="${xmlSchemas.src}"/>
<property name="jaxbJar.file" value="jaxbPerson.jar"/>
<property name="schema.file" value="${schema.root}/Person.xsd"/>
<property name="bindings.file" value="${schema.root}/SubstitutionsV2.xjb"/>
<property name="package" value="com.onerail.orion.reservation.xml.messages.coreorion"/>
</target>


<target name="copy_jaxb_jar">
<echo message="Copying ${jaxbJar.file} into ${src}/WEB-INF/lib"/>
<copy file="${jaxb.tmp}/${jaxbJar.file}" todir="${src}/WEB-INF/lib"/>
</target>



Marshalling

public class TestPerson
{
ObjectFactory factory = new ObjectFactory();
Person personObj = factory.createPerson();
personObj.setName("Jane");
personObj.setAge("10");
personObj.setAddress("Martin Place, Sydney");

JAXBContext jaxbContext = JAXBContext.newInstance(
"com.mytest.example");
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
new Boolean(true));
marshaller.marshal(personObj, new FileOutputStream(
"personInstance.xml"));

}

Optionally, you can validate an object you are serializing
against a JAXB-generated content tree. To do that, use the
Validator object as follows:

Validator validator = jaxbContext.createValidator();
Validator.validate(person);

Here for example, is what a sample listing of an XML serialization of
a Person object looks like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<name>Jane</name>
<age>10</age>
<address>Martin Place, Sydney</address>
</person>



Unmarshalling

Here is a code example that unmarshals data from an XML file to
a JAXB-generated instance:

import javax.xml.bind.*;

JAXBContext jc = JAXBContext.newInstance(
"com.mytest.example");
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setValidating(true);
Peson instance = (Peson)unmarshaller.unmarshal(
new File("personInstance.xml"));

System.out.println("Name is: " + instance.getName());
System.out.println("Age is: " + instance.getAge());
System.out.println("Address is: " + instance.getAddress());

If setValidating() is set to true, the JAXB runtime API
validates the XML that contains the instance data against the
constraints specified by the XML schema that was submitted to
the binding compiler. If the data is invalid, the validation
fails and generates a runtime exception.

Utility method to convert a String XML to Object XML

public static final String JAXBCONTEXT_PACKAGE = "com.test.xml.messages.core"; // package where all generated JAXB classes exist

public static Object convStringToXmlObj(String toConvert) throws JAXBException, IOException
{
ByteArrayInputStream bais = new ByteArrayInputStream(toConvert.getBytes());

Unmarshaller unmarshaller = JAXBContext.newInstance(JAXBCONTEXT_PACKAGE).createUnmarshaller();
unmarshaller.setValidating(true);
Object toReturn = unmarshaller.unmarshal(bais);
bais.close();
return toReturn;
}

Utility method to convert a Object XML to String XML

public static String convXmlObjToString(Object toConvert) throws JAXBException, IOException
{

ByteArrayOutputStream baos = new ByteArrayOutputStream();

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(toConvert, baos);

baos.close();

return baos.toString("UTF-8");
}

Utility method to convert a File to Object XML

public static Object convFileToXmlObj(String fileLocation) throws JAXBException, IOException
{
File file = new File(fileLocation);
byte[] b = new byte[(int) file.length()];
FileInputStream in = null;
try
{
in = new FileInputStream(file);
in.read(b, 0, b.length);
}
catch(java.io.FileNotFoundException e1)
{
logger.error("File not found");
}
catch(java.io.IOException e2)
{
logger.error("I/O Exception");
}
finally
{
if(in != null)
{
try
{
in.close();
}
catch(java.io.IOException e)
{
logger.error("I/O Exception");
}
}
}
return convStringToXmlObj(new String(b));
}



No comments: