Saturday, August 21, 2010

JAXB - Java Architecture for XML Binding - The OXM tool !

JAXB is  OXM - Object XML Mapping  :) ! it is what ORM is to relational databases.
Hibernate and othe ORM tools is to Databases what JAXB is to XML.
 
This simple tutorial is for people who are :
1)  Sick and tired of parsing XML with/without using any library.
2)  Who have never used JAXB and want to get started.

I used to work for a company where we used to un-marshall (parse)  XML using a library written in Delphi after installing it into Borland C++ Builder. My manager told me "It is easier to write XML data out than to read it in. If i were you i would have written the code myself". He was right to a certain extent. I wrote the code for writing XML data out to another system and I must say the whole process was quite simple except for the initial few mistakes i made for the tiny XML Writer library.

I used the same XML writer library i built there for a couple of other small hobby projects and it has worked well with not changes needed to the core library. Here's the header file for the simple XML writer library i wrote then.

#ifndef BlueXmlWriterH
#define BlueXmlWriterH

#define START_ANGLE_BRACKET "<"
#define CLOSE_ANGLE_BRACKET ">"
#define BACK_SLASH_MARK "/"
#define SPACE " "
#define EQUAL "="
#define DOUBLE_QUOTE "\""

#include<string>
using namespace std;

class BlueXmlWriter
{
    public:
            BlueXmlWriter(string &XmlBuffer);
            BlueXmlWriter(char *XmlBufferToWriteTo);
            bool AddOpeningTag(const char *StartTag);
            bool AddOpeningTag(const char *StartTag, const char **attr);
            bool AddNode( const char *ElementName, const char *ElementData);
            bool AddNode( const char *ElementName, int IntElementData);
            bool AddClosingTag(const char *EndTag);
            bool AddData(const char *Data);
            bool AddData(int Data);
            inline int GetBufferSize() { return mXmlBytesWritten; }
            char *  GetXmlBuffer() { return mXmlBuffer; }
    private:
            char *mXmlBuffer;
            int mXmlBufferSize;
            int mXmlBytesWritten;

};

It was used like :
lXmlWriter = new BlueXmlWriter(lBuffer); 
lXmlWriter->AddOpeningTag(BLUEMANTRA_START_XML);
lXmlWriter->AddNode(BLUEMANTRA_EVENT,BLUEMANTRA_EV_PRESENCE);

Later on, when i moved on to another company there was this requirement to send and receive XML data to and from a Billing server for charging prepaid mobile customers. Then, i used the DocumentBuilder class to parse the XML , writing XML was still handwritten code (My previous managers advice except that the code was now ported to Java). It worked fine and I found it to have more fine grained control for writing XML when you have your own library.
http://download.oracle.com/javase/1.4.2/docs/api/javax/xml/parsers/DocumentBuilder.html  was used for parsing the incoming XML stream. This worked fine too. But it was later on in life that i found JAXB.

I have used JAXB for a web application project and i found it to be immensely helpfull in projects which have a lot of XML marshalling and unmarshalling. The bad news was it does marshalling too - so it was time to say goodbye to my XML Writer library.

The basic steps involved in using JAXB is as follows : (I have compared it to ORM in bold just to help reader relate to it better, if you do not know or have not used any ORM tools before please skip the sentences in bold)

1) You create a XSD (XML Schema definition file) and write your XML Schema. XML Schemas define the structure of your XML. I found this link (XSD Tutorial)  to be particularly usefull for writing the XSD myself. I had no clue how to write one before. To relate this to ORM (Object Relational mapping with Hibernate)  you create the .hbm.xml file to map classes to tables and columns in the database.

2) Use the binding compiler which comes with JAXB to convert this XML Schema to POJO's which represent your XML Schema definitions.You could relate this to some ORM tools creating the POJO's from the XML file which describes how your classes map to the database table columns. 

3) Create the JAXB Context which will be used to create the Marshaller and UnMarshaller. This is equivalent to the creation of a SessionFactory with the database when it comes to ORM.


4) Use the POJO's generated by the binding compiler to marshall and unmarshall the XML.This is similar to retreiving (Reading) and saving (Writing)  objects to the databse in ORM.

Ok, lets get started then. For this tutorial we will use the example of a course and the subjects which are part of the course. Let us first try to write the XSD for this. Please read up on this link XML Schema Tutorial if you have no prior experience with XML Schema Definitions.

XSD can be used to describe the XML structure in detail. It can be used to apply constraints to the data in the XML. For example if there is an element called age as <Age>45</Age> we could write the XSD so that the value of Age cannot be outside the range say 0 to 120. And if the XML you are unmarshalling is outside this range you get an exception raised by JAXB. Just imagine the extra code you would have to write to check for such errors.

Moving to our XSD for the Course. A course can have one or more subjects as part of it. We will write the XSD for this relationship as below. 

First we describe the Subject. A Subject has a name. 

<xs:simpleType name="Subject" >
<xs:restriction base="xs:string" >
<xs:length value="15"/>
</xs:restriction>
</xs:simpleType>


This describes the Subject as a simple type with the data type restricted to a string and the length ( number of characters ) limited to 15.  Example could be Geography, Social Studies, length to be less than 15 chars.


Now let us describe the Course in XSD. 


<xs:complexType name="Course">
<xs:sequence>
<xs:element name="Name" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="CSubject" type="Subject" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>

The above definition of the XML Schema for Course says "Every Course has  a name and one or more Subjects".
It has a minimum of one and maximum can be any number, we could have easily restricted the maximum number to 5 with maxOccurs="5". This is how our application.xsd file looks now :


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" >



<!-- XML Elements  - This is an XML Comment like /* */ in  C/ C++ / Java  -->
<xs:element name="CSubject" type="Subject"/>
<xs:element name="CCourse" type="Course"/>

<!-- Defining Subject -->
<xs:simpleType name="Subject" >
<xs:restriction base="xs:string" >
<xs:length value="15"/>
</xs:restriction>
</xs:simpleType>

<!-- Defining Course -->
<xs:complexType name="Course">
<xs:sequence>
<xs:element name="Name" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="CSubject" type="Subject" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>

 

 </xs:schema>

Now, all that is left is to generate classes from the XSD and then use them in our application. Let us now use the Binding Compiler to generate the classes for us. The Binding  compiler is called xjc and is used as follows.
I found it in my jdk installation directory C:\Program Files\Java\jdk1.6.0_05\bin , I do not remember whether it comes with the standard jdk installation or i copied it from somewhere, in anyways it is quite easy to find this on the internet with a search. So copy it to the bin directory of your jdk installation so that it can be used from anywhere assuming you have the PATH variable set properly.
It takes the .xsd file as input and generates POJO's (Plain Old Java Objects)  reperesenting your domain objects plus an ObjectFactory which you would use to create the objects.

C:\eclipseprojects\workspace\JAXB_TEST\src> xjc -nv application.xsd  -p jaxb



 



The command instructs xjc to create the POJO's in a package called jaxb, you could use any package structure like com.xyz.xml.jaxb the compiler will create the directory structure matching your package name.In this case all the generated files go into the jaxb directory.


Here's the xjc generate Course.java file for reference.


/**
 * <p>Java class for Course complex type.
 *
 


public class Course {

    @XmlElement(name = "Name", required = true)
    protected String name;
    @XmlElement(name = "CSubject", required = true)
    protected List<String> cSubject;

    /**
     * Gets the value of the name property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getName() {
        return name;
    }

    /**
     * Sets the value of the name property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setName(String value) {
        this.name = value;
    }

    /**
     * Gets the value of the cSubject property.
      * For example, to add a new item, do as follows:
     * <pre>
     *    getCSubject().add(newItem);
     * </pre>
     *
     *
     * <p>
     * Objects of the following type(s) are allowed in the list
     * {@link String }
     *
     *
     */
    public List<String> getCSubject() {
        if (cSubject == null) {
            cSubject = new ArrayList<String>();
        }
        return this.cSubject;
    }
}



Here's ObjectFactory.java 

/**
 * This object contains factory methods for each
 * Java content interface and Java element interface
 * generated in the jaxb package.
 * <p>An ObjectFactory allows you to programatically
 * construct new instances of the Java representation
 * for XML content. The Java representation of XML
 * content can consist of schema derived interfaces
 * and classes representing the binding of schema
 * type definitions, element declarations and model
 * groups.  Factory methods for each of these are
 * provided in this class.
 *
 */
@XmlRegistry
public class ObjectFactory {

    private final static QName _CSubject_QNAME = new QName("", "CSubject");
    private final static QName _CCourse_QNAME = new QName("", "CCourse");

    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: jaxb
     *
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link Course }
     *
     */
    public Course createCourse() {
        return new Course();
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "", name = "CSubject")
    public JAXBElement<String> createCSubject(String value) {
        return new JAXBElement<String>(_CSubject_QNAME, String.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link Course }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "", name = "CCourse")
    public JAXBElement<Course> createCCourse(Course value) {
        return new JAXBElement<Course>(_CCourse_QNAME, Course.class, null, value);
    }

}



Now that we have the domain objects and the factory, all that is left is to use JAXBContext to create a marshaller and marshall some XML data from the domain objects. Back to our example we will create a Course object named "Software Design Patterns" and add subjects called 
"Design Principles" , "Creational Patterns" , "Behavioral patterns" ,"Structural Patterns"

MARSHALLING  ->  XML FROM DOMAIN OBJECTS  :

Before we start, as mentioned above we need to create a Marshaller to marshal XML Content from our domain objects using the JAXBContext class.

The JAXBContext class provides the client's entry point to the JAXB API. It provides an abstraction for managing the XML/Java binding information necessary to implement the JAXB binding framework operations: unmarshal, marshal and validate. 

// jaxb is the package (directory) where all our xjc generated files reside  
JAXBContext mJAXBContextInstance = JAXBContext.newInstance( "jaxb" );
// create the Marshaller 
Marshaller lMarshaller = mJAXBContextInstance.createMarshaller();
/* Create the domain object factory */
ObjectFactory lFactory = new ObjectFactory();
/* Create the Course object */
Course lCourse = lFactory.createCourse();  /* We can use new Course() instead */
lCourse.setCName("Software Design Patterns");   /* Name of the course */

lCourse.getCSubject().add("Design Principles");   
lCourse.getCSubject().add("Creational Patterns");  /* Subjects in the course */
lCourse.getCSubject().add("Structural Patterns");
lCourse.getCSubject().add("Behavioral Patterns");

/* The following line of code is needed to marshall the domain object into XML format, without this
the Marshalling fails */

JAXBElement<Course> lCourse1 = lFactory.createCCourse(lCourse);
try {
            lMarshaller.marshal(lCourse1, System.out);
} catch (JAXBException e) {
            e.printStackTrace();
}

The output from running this code is :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CCourse>
<Name>Software Design Patterns</Name>
<CSubject>Design Principles</CSubject>
<CSubject>Creational Patterns</CSubject>
<CSubject>Structural Patterns</CSubject>
<CSubject>Behavioral Patterns</CSubject>
</CCourse>

Isn't this cool ? We have NOT written a line of code to build the XML Content we are seeing here. It is all taken care by JAXB. 


UNMARSHALING -> DOMAIN OBJECTS FROM XML  :
 
Similarly unmarshalling XML to Domain objects works the same way you create an UnMarshaller using the JAXBContext class. 

We have our Course.xml here as below :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CCourse>
<Name>Software Design Patterns</Name>
<CSubject>Design Principles</CSubject>
<CSubject>Creational Patterns</CSubject>
<CSubject>Structural Patterns</CSubject>
<CSubject>Behavioral Patterns</CSubject>
</CCourse>

We will create an UnMarshaller and parse this XML file to domain objects. Code follows :

JAXBContext lContext = null; 
Unmarshaller lUnMarshaller = null;
        try {
                lContext = JAXBContext.newInstance("jaxb");
                lUnMarshaller = lContext.createUnmarshaller();
            } catch (JAXBException e) {
                  e.printStackTrace();
            }
       
 FileReader lReader = null;
  try {
            lReader = new FileReader(new File("Course.xml"));
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
       
try {
            JAXBElement<Course> lCourse2 = (JAXBElement<Course>)  lUnMarshaller.unmarshal(lReader);
            Course lCourse3 = lCourse2.getValue();
            System.out.println("Get the name of the course : " + lCourse3.getName());
            System.out.println("Get the Subjects count " + lCourse3.getCSubject().size());
            Iterator<String> lIter = lCourse3.getCSubject().iterator();
            while(lIter.hasNext()) {
                System.out.println("Subject is " + lIter.next());
            }
       
}
}


The output from the code is below :

Get the name of the course : Software Design Patterns
Get the Subjects count 4
Subject is Design Principles
Subject is Creational Patterns
Subject is Structural Patterns
Subject is Behavioral Patterns



That's it we UnMarshalled (parsed) the XML file into our domain object Course and we saw that the properties of the Course object automagically populated with the contents by JAXB.

FINAL THOUGHTS : 

JAXB is a very handy tool to manipulate XML Content use it in large projects and when the XML structure changes you do not need to write any code, just run xjc again on the new XSD. We have just seen some parts of JAXB, there are features to handle errors dynamically. We saw how to Marshall and UnMarshal XML , there are options to validate the XML as well . Hope this blog helped you with JAXB. 


-  Voice~Streams










Followers

About Me

I'm a software developer with interests in Design Patterns, Distributed programming, Big Data, Machine Learning and anything which excites me. I like to prototype new ideas and always on the lookout for tools which help me get the job done faster. Currently, i'm loving node.js + Mongodb.