Tuesday, August 13, 2013

XSD to Java Objects to XML using JAXB

XSD to Java Objects to XML using JAXB


About

This post is step by step tutorial about the conversion of an XSD file to first java objects using xjc and then to convert the java objects to XML using JAXB. Also to check if the final generated XML is compliant with the XSD.

Pre-requisites
  • jdk1.5 or higher installed on your system. Set JAVA_HOME path to the folder location of jdk1.5 in your system. Eg: C:\jdk1.5.0_10.
  • JAXB 2.2.4 (You can download this from here). To use this, first extract it to a location on your system.  Set the JAXB_HOME path to the folder in which you have extracted the content (e.g. C:\jaxb).  You are all set to start using JAXB for your XML related work in JAVA.
Explanation:
  1. Our first step would be create the java objects from the XSD using xjc commnad. 
          Sample XML Schema Customer.xsd:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Customer" type="Customer"/>
<xsd:complexType name="Customer">
  <xsd:all>
    <xsd:element name="Profile" type="Profile" minOccurs="1" maxOccurs="1"/>
    <xsd:element name="Address" type="Address" minOccurs="1" maxOccurs="1"/>
  </xsd:all>
</xsd:complexType>
<xsd:complexType name="Profile">
  <xsd:all>
    <xsd:element name="Name" minOccurs="1">
      <xsd:simpleType>
        <xsd:restriction base="xsd:string">
          <xsd:maxLength value="100"/>
        </xsd:restriction>
      </xsd:simpleType>
    </xsd:element>
    <xsd:element name="Email" minOccurs="0">
      <xsd:simpleType>
        <xsd:restriction base="xsd:string">
          <xsd:maxLength value="50"/>
        </xsd:restriction>
      </xsd:simpleType>
    </xsd:element>
    <xsd:element name="Phone" maxOccurs="1">
      <xsd:simpleType>
        <xsd:restriction base="xsd:string">
          <xsd:maxLength value="12"/>
        </xsd:restriction>
      </xsd:simpleType>
    </xsd:element>
  </xsd:all>
</xsd:complexType>
<xsd:complexType name="Address">
  <xsd:all>
    <xsd:element name="FlatNo" minOccurs="1">
      <xsd:simpleType>
        <xsd:restriction base="xsd:string">
          <xsd:maxLength value="100"/>
        </xsd:restriction>
      </xsd:simpleType>
    </xsd:element>
    <xsd:element name="Street" minOccurs="1">
      <xsd:simpleType>
        <xsd:restriction base="xsd:string">
          <xsd:maxLength value="100"/>
        </xsd:restriction>
      </xsd:simpleType>
    </xsd:element>
    <xsd:element name="Landmark" minOccurs="0">
      <xsd:simpleType>
        <xsd:restriction base="xsd:string">
          <xsd:maxLength value="100"/>
        </xsd:restriction>
      </xsd:simpleType>
    </xsd:element>
  </xsd:all>
</xsd:complexType>
</xsd:schema>

Create a file Customer.xsd and place the above content in it. Now open the command prompt in the location of the Customer.xsd, now run he below command:

%JAXB_HOME%\bin\xjc -p Customer C:\sample\schema\Customer.xsd.

Replace C:\sample\schema\Customer.xsd with your xml Schema location. 

On running the above command a new Folder named Customer is created in the location where you have opened the command prompt with the files as:

According to the xsd Structure 3 files specific to our xsd gets generated Customer, Address and Profile. ObjectFactory is a factory class that gets generated which is useful for XML generation which we will see later.

2. Now create a new Workspace in the JAVA IDE of your choice, 
also create a new package and paste the above generated files and the 
Customer.xsd previously made in that package.

      The package downloaded from JAXB website contains jaxb-api.jar which you have to 
include in the buildpath of your project.

Now create a Main.java as below

package xsdtoxml.test;

import java.io.File;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.SAXException;

public class Main {

 public static void main(String r[]) throws SAXException, JAXBException {
  /**
   * Create ObjectFactory. It is a general factory class
   */

  ObjectFactory factory = new ObjectFactory();

  // Create the objects of the Address, Customer and Profile classes
  Profile profile = factory.createProfile();
  Address address = factory.createAddress();
  Customer customer = factory.createCustomer();

  /**
   * Now populate dummy data into profile and address classes
   */

  profile.setEmail("abc@rr.com");
  //profile.setName("Person Name");
  profile.setPhone("12345");

  address.setStreet("xyz");
  address.setFlatNo("100");
  address.setLandmark("near Qwerty Mall");

  customer.setAddress(address);
  customer.setProfile(profile);

  SchemaFactory sf = SchemaFactory
    .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
  Schema schema = sf
    .newSchema(new File("src/xsdtoxml/test/Customer.xsd"));

  /**
   * Create context which is in turn used while creating the marshall and
   * unmarshall objects for XML generation
   */

  JAXBContext context = JAXBContext.newInstance("xsdtoxml.test");

  /**
   * This class represents information about an Xml Element from both the
   * element declaration within a schema and the element instance value
   * within an xml document
   */

  JAXBElement<Customer> element = factory.createCustomer(customer);

  /**
   * The Marshaller class is responsible for governing the process of
   * serializing Java content trees back into XML data.
   */

  Marshaller marshaller = context.createMarshaller();

  /*
   * You can set the namespace path of your xml file using below line
   */
  marshaller.setProperty("jaxb.noNamespaceSchemaLocation",
    "C:/Desktop/XMLSchema");

  /**
   * Set the schema so as to check if the newly generated XML is in
   * compliant with our xsd
   * 
   */
  marshaller.setSchema(schema);

  marshaller.setEventHandler(new CustomerValidationEventHandler());
  marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
  marshaller.marshal(element, System.out);

 }
}


Now to validate if the minOccurs, maxOccurs, length and other such restrictions mentioned in XSD are followed in the XML we write a Validator 
CustomerValidationEventHandler.java

package xsdtoxml.test;

import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;

public class CustomerValidationEventHandler implements ValidationEventHandler {

 @Override
 public boolean handleEvent(ValidationEvent ve) {
  // TODO Auto-generated method stub
  if (ve.getSeverity()==ValidationEvent.FATAL_ERROR ||  
             ve .getSeverity()==ValidationEvent.ERROR){
             ValidationEventLocator  locator = ve.getLocator();
             //Print message from valdation event
             System.out.println("Invalid Customer: " 
                                + locator.getURL());
             System.out.println("Error: " + ve.getMessage());
             //Output line and column number
             System.out.println("Error at column " + 
                                locator.getColumnNumber() + 
                                ", line " 
                                + locator.getLineNumber());
          }
          return true;
 }

} 

I have included comments in the code for better understanding. Please do refer the comments.

Output:

1. XML is fully compliant with the XSD

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:/Desktop/XMLSchema">
    <Profile>
        <Name>Person Name</Name>
        <Email>abc@rr.com</Email>
        <Phone>12345</Phone>
    </Profile>
    <Address>
        <FlatNo>100</FlatNo>
        <Street>xyz</Street>
        <Landmark>near Qwerty Mall</Landmark>
    </Address>
</Customer>

2. XML is not compliant with XSD. For example we remove the profile.setName() which is mandatory.
    The validator catches this error and gives us the output as:

   Invalid Customer: null
   Error: cvc-complex-type.2.4.b: The content of element 'Profile' is not complete. One of '{Name}' is expected.


Conclusion:

That's it. Feel free to post in your comments in case of any queries.


2 comments:

  1. very detailed explanation. Nice work! bit it would be nice if you explained what xjc is..in short what you plan to do. So if some is just surfing through the net and sees this page (like me) they will understand what the aforesaid technology is capable of doing. Keep up the good work. Thank you

    ReplyDelete
    Replies
    1. Thanks for your feedback...i would certainly take care in future....

      Delete