Pages

Tuesday, June 12, 2012

Marshalling and Unmarshalling XML data using JAXB

JAXB is a Java Api for for Marshalling and UnMarshalling XML Data. The idea is that you bind an XML Schema to an object and then use this schema-bound object to marshall or unmarshall XML Data. Using this API you can transform any XML document into a java object and any java object into an XML document.

So how is it done. Here is a summery of the steps:

  • Create an XSD file that describes structure of your XML document
  • Execute xjc command from command prompt and pass package and .xsd file as parameters

Well that's it really, this will generate the java files in the given package. It will generate the complete package/directory structure according to the given parameter.

Let's try that using an example XSD file.


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="Contacts">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" ref="Contact"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="Contact">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="FirstName"/>
        <xs:element ref="LastName"/>
        <xs:element ref="PhoneNumber"/>
      </xs:sequence>
      <xs:attribute name="id" use="required" type="xs:integer"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="FirstName" type="xs:string"/>
  <xs:element name="LastName" type="xs:string"/>
  <xs:element name="PhoneNumber" type="xs:integer"/>
</xs:schema>

Save the above as contacts.xsd and then generate Java files using JAXB compiler. * xjc [file.xsd] -p [package]


> xjc contacts.xsd -p test.jaxb.contact
parsing a schema...
compiling a schema...
test\jaxb\contact\Contact.java
test\jaxb\contact\Contacts.java
test\jaxb\contact\ObjectFactory.java

This will create all the required classes for you in the given package. In our case you can see the package name is test.jaxb.contact so the classes are generated in this package. The compiler will also create necessory folders for you to place java fiels, i.e. all you need to do is to provide a package name and the rest is automatically handled.

Now let's do the Marshalling and UnMarshalling of some XML data using these generated java files. Marshalling means writing XML data and UnMarshalling means reading XML data. We will do the UnMarshalling first and the we will see how to do the Marshalling.

Un-Marshalling:

Now lets create a sample XML file and load the data in the XML file to our java objects. I am creating a sample file with some famous hollywood names.


<?xml version="1.0" encoding="UTF-8"?>
<Contacts>
    <Contact id="2002">
        <FirstName>Will</FirstName>
        <LastName>Smith</LastName>
        <PhoneNumber>0192824546</PhoneNumber>
    </Contact>
    <Contact id="2008">
        <FirstName>Bruce</FirstName>
        <LastName>Willis</LastName>
        <PhoneNumber>0138542756</PhoneNumber>
    </Contact>
    <Contact id="2029">
        <FirstName>Julia</FirstName>
        <LastName>Roberts</LastName>
        <PhoneNumber>0138452168</PhoneNumber>
    </Contact>
    <Contact id="2086">
        <FirstName>Kate</FirstName>
        <LastName>Hudson</LastName>
        <PhoneNumber>0169485324</PhoneNumber>
    </Contact>
</Contacts>

Now create a simple java file to Unmarshal this XML data. I am not going into the details of File Handlilng, here is a method that will take the InputStream that you can create for the file and then using that InputStream it will Unmarshal the XML data.


// Create an Input Stream for the XML file
// and pass the Stream as a method parameter
private static void unmarshalXMLData(InputStream is) throws Exception{

    // Context is the name of package 
    String context = "test.jaxb.contact";

    // Create an instance of JAXB Context
    JAXBContext jContext = JAXBContext.newInstance(context);


    // Unmarshal the data from InputStream
    Contacts contacts = (Contacts) jContext.createUnmarshaller().unmarshal(is);

    List contact = contacts.getContact();

    // Lets see the results.
    for (Contact c : contact) {
       System.out.println(c.id+""+c.firstName+""+c.lastName+""+c.phoneNumber);
    }
}

And when this method is executed, it produced the following results for the given XML file.


2002 Will Smith 192824546
2008 Bruce Willis 138542756
2029 Julia Roberts 138452168
2086 Kate Hudson 169485324

OK, so the Unmarshalling was easy, we successfully created some java objects and loaded data into these java objects using JAXB. Now let's see how to pulish the XML data from your java objects.

Marshalling:

Let's stick to the same conventions, just for the ease of use. To generate the XML data from our java classes, all we need to do is to tell the Marshaller what it needs to write and where.

Again I am not going into the details of File Handling, here is a method that takes the PrintWriter object as a parameter and writes the XML data to that writer.


private static void marshalXML(PrintWriter out) throws Exception{

  // Context is the name of package  
  String context = "test.jaxb.contact";
  // Initialise JAXB Context
  JAXBContext jc = JAXBContext.newInstance(context);

  // Always use factory methods to initialise XML classes
  ObjectFactory factory = new ObjectFactory();
  Contacts contacts = factory.createContacts();

  // Now create some sample contacts 
  Contact c = new Contact(); 
  c.setId(new BigInteger("2098"));
  c.setFirstName("Jonny");
  c.setLastName("Depp");
  c.setPhoneNumber(new BigInteger("2646215098"));
   
  // Always use the get methods for adding more objects to a collection
  contacts.getContact().add(c);
   
  c = new Contact();
  c.setId(new BigInteger("2168"));
  c.setFirstName("Anthony");
  c.setLastName("Hopkins");
  c.setPhoneNumber(new BigInteger("2646546879"));
  
  // Add the new Object to collection
  contacts.getContact().add(c);

  // Now Create JAXB XML Marshallar
  Marshaller m = jc.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );

  // Write the XML File
  m.marshal(contacts, out);

}

This method will initialize the Marshaller and will marshal the data to the PrintWriter. Fortunately PrintWriter is not the only option for you. You can also send data to any OutputStream or even to a ResultSet. Executing this piece of code will generate an XML file that will look something like this:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Contacts>
   <Contact id="2098">
     <FirstName>Jonny</FirstName>
     <LastName>Depp</LastName>
     <PhoneNumber>2646215098</PhoneNumber>
   </Contact>
   <Contact id="2168">
     <FirstName>Anthony</FirstName>
     <LastName>Hopkins</LastName>
     <PhoneNumber>2646546879</PhoneNumber>
   </Contact>
</Contacts>

That's it really. From here on you can now create your own XSD documents to take advantage of JAXB. What I really like about that is that I dont have to worry about the XML formatting of my data when I am reading or writing XML data to an external resource. The JAXB API takes care of almost all of the possible scenarios where you have to manually handle the text in an XML document.

As far as XML is concirned, almost everyone now a days os familiear with it, however, not everyone feels comfortable with the XML Schema fiels (XSD). For those of you who are not comfortable with creating the XSD files, I would suggest using the XML to XSD converters. There are loads of converters available there on the internet if you search for the term XML to XSD.

My favourite is the trang-xml XML to XSD converter. This is a very simple jar file that you can use to generate an XSD document from any XML. Using Trang is very simple, all you need to do is to execute the following command from the command prompt and it will generate an XSD file for you from the given XML file.



java -jar trang.jar contacts.xml contacts.xsd

This sounds excellent, however, whenever you are using any XML to XSD generator, always use it with caution. The automated process that will identify the data types from your XML document to generate an XSD, is never going to be absolutely correct. There are always areas where you have to make neccessory changes to the XSD file after generating it using any convertor.