Friday, November 22, 2013

Marshalling wih JAXB



Introduction to JAXB starts with:

Java Architecture for XML Binding (JAXB) provides a fast and convenient way to bind XML schemas and Java representations, making it easy for Java developers to incorporate XML data and processing functions in Java applications.

This blog is not going to touch on XML Schema part, I found JAXB to be excellent architecture for binding XML and Java objects. To compile and run the examples, you can use any version of JDK6 and above that includes JAXB2.X.

JAXB provides methods for unmarshalling (reading) XML instance documents into Java content trees, and then marshalling (writing) Java content trees back into XML instance documents.

In simple words, JAXB offers you APIs for converting your Java objects trees to XML document and vice versa through marshaling and unmarshalling processes.

 





In order to facilitate this conversion (for ex: marshalling), we need to map our Java instances with corresponding XML element. In simple words, we need to explain the JAXB that ‘productName’ attribute in my class has to be mapped with <productName> element, similarly ‘quantity’ attribute will be mapped with <quantity> element in XML document.

private String productName; // to be mapped with <productName>
private int quantity; // to be mapped with <quantity>

This mapping could be achieved by writing some JAXB annotations in Java classes to indicate XML element name for each attribute. For our case, the annotations would be:

@XmlElement(name = "productName") 
private String productName;
@XmlElement(name = "quantity") 
private int quantity;

If your Java attribute name and XML element name is same (as in above scenario), you don’t even need to specify this annotation, JAXB would automatically create an XML Element with same name as that of attribute in Java class. So we could simply write our attributes as below and let JAXB handle the conversion (or marshaling).

private String productName;
private int quantity;

Let’s create a simple class for which we will use JAXB to marshal this instance for creating corresponding XML document.

Item.java

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Item {
       public String getProductName() {
              return productName;
       }

       public void setProductName(String productName) {
              this.productName = productName;
       }

       public int getQuantity() {
              return quantity;
       }

       public void setQuantity(int quantity) {
              this.quantity = quantity;
       }

       private String productName;
       private int quantity;
}

JAXBMarshaler.java
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class JAXBMarshaler {
       public static void main(String[] args) throws Exception{

              Item item = new Item();
              item.setProductName("Entrepreneur - March 2013 edition");
              item.setQuantity(1);
             
              JAXBContext jaxbContext = JAXBContext.newInstance(Item.class);
              Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
              jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
              jaxbMarshaller.marshal(item, System.out);
       }
}

Compiling above classes and running JaxmMarshaler produces below output:
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<item>
    <productName>Entrepreneur - March 2013 edition</productName>
    <quantity>1</quantity>
</item>

In Item class, we have just one annotation ‘@XmlRootElement’. As per JAXB tutorial,
If a Java class has a straight forward mapping to XML schema (or XML document), 
XmlRootElement is the only annotation you’ve to make.
 
# If I’ve more than one Items in a Purchase Order, class definition would change

PurchaseOrder.class
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class PurchaseOrder {

                private String orderNumber;
                private List<Item> items = new ArrayList<Item>();
               
                public String getOrderNumber() {
                                return orderNumber;
                }

                public void setOrderNumber(String orderNumber) {
                                this.orderNumber = orderNumber;
                }

                public List<Item> getItems() {
                                return items;
                }

                public void setItems(List<Item> items) {
                                this.items = items;
                }
}

Item.java

import javax.xml.bind.annotation.XmlType;

public class Item {
       public String getProductName() {
              return productName;
       }

       public void setProductName(String productName) {
              this.productName = productName;
       }

       public int getQuantity() {
              return quantity;
       }

       public void setQuantity(int quantity) {
              this.quantity = quantity;
       }

       private String productName;
       private int quantity;
}

JAXBMarshaler.java

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class JAXBMarshaler {
       public static void main(String[] args) throws Exception{

              List<Item> items = new ArrayList<Item>();
             
              Item item = new Item();
              item.setProductName("Entrepreneur - March 2013 edition");
              item.setQuantity(1);
             
              Item item2 = new Item();
              item2.setProductName("Food and Fun - April 2013 edition");
              item2.setQuantity(1);
             
              items.add(item);
              items.add(item2);
             
              PurchaseOrder po = new PurchaseOrder();
              po.setItems(items);
              po.setOrderNumber("1-1XY6FG");
             
              JAXBContext jaxbContext = JAXBContext.newInstance(PurchaseOrder.class);
              Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
              jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
              jaxbMarshaller.marshal(po, System.out);
       }
}

Compiling and running JAXBMarshalar produces expected XML with PurchaseOrder as top document element. We've also moved @XmlRootElement annotation toPurchaseOrder.java

Output.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<purchaseOrder>
    <items>
        <productName>Entrepreneur - March 2013 edition</productName>
        <quantity>1</quantity>
    </items>
    <items>
        <productName>Food and Fun - April 2013 edition</productName>
        <quantity>1</quantity>
    </items>
    <orderNumber>1-1XY6FG</orderNumber>
</purchaseOrder>

 In XML document, all the Items are appearing in <items> element, If you want to create a container element <items> that contains <item> sub-elements, we need to modify our classes to follow XML structure.

Item.java

import javax.xml.bind.annotation.XmlType;

public class Item {
       public String getProductName() {
              return productName;
       }

       public void setProductName(String productName) {
              this.productName = productName;
       }

       public int getQuantity() {
              return quantity;
       }

       public void setQuantity(int quantity) {
              this.quantity = quantity;
       }

       private String productName;
       private int quantity;
}

Items.java

import java.util.ArrayList;
import java.util.List;

public class Items {
       private List<Item> item = new ArrayList<Item>();

       public List<Item> getItem() {
              return item;
       }

       public void setItem(List<Item> item) {
              this.item = item;
       }

}

JAXBMarshalar.java

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class JAXBMarshaler {
       public static void main(String[] args) throws Exception{

              List<Item> itemList = new ArrayList<Item>();
             
              Item item = new Item();
              item.setProductName("Entrepreneur - March 2013 edition");
              item.setQuantity(1);
             
              Item item2 = new Item();
              item2.setProductName("Food and Fun - April 2013 edition");
              item2.setQuantity(1);
             
              itemList.add(item);
              itemList.add(item2);
             
              Items items = new Items();
              items.setItem(itemList);
             
              PurchaseOrder po = new PurchaseOrder();
              po.setItems(items);
              po.setOrderNumber("1-1XY6FG");
             
              JAXBContext jaxbContext = JAXBContext.newInstance(PurchaseOrder.class);
              Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
              jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
              jaxbMarshaller.marshal(po, System.out);
       }
}

Output.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<purchaseOrder>
    <items>
        <item>
            <productName>Entrepreneur - March 2013 edition</productName>
            <quantity>1</quantity>
        </item>
        <item>
            <productName>Food and Fun - April 2013 edition</productName>
            <quantity>1</quantity>
        </item>
    </items>
    <orderNumber>1-1XY6FG</orderNumber>
</purchaseOrder>

Summary

In Java classes, there is just one annotation for PurchaseOrder.java that makes this whole thing work. Quite simple! If we need to design our Java classes from XML, simply creating our classes by following the XML document structure would suffice.

References:

No comments:

Post a Comment