Monday, November 25, 2013

Unmarshalling with JAXB



Unmarshalling with JAXB was little complicated than marshaling. I started with my previous post and tried keeping my classes simple by using minimal set of annotations. Our goal is to create set of Java classes to parse below XML document.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Album>
       <Name>My trip to Hawai</Name>
       <Date>11/03/2012</Date>
       <Pictures>
              <Picture type="JPEG">
                  <Name>Foodoo</Name>
                  <Place>At Beach</Place>
                  <Time>11/01/2013 11:15:39 AM</Time>
              </Picture>
              <Picture type="GIF">
                  <Name>Dilloo</Name>
                  <Place>In hotel</Place>
                  <Time>11/01/2013 07:50:56 PM</Time>
              </Picture>                           
       </Pictures>
</Album>


Following the XML structure, I created three classes


 Album.java
import java.util.Date;

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

@XmlRootElement(name="Album")
public class Album {
  
    private String name;
  
    private Date date;
  
    private Pictures pictures;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date value) {
        this.date = value;
    }

    public Pictures getPictures() {
        return pictures;
    }

    public void setPictures(Pictures value) {
        this.pictures = value;
    }
}



Picture.java
import java.util.Date;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

public class Picture {

    private String name;
    private String place;
    private Date time;

    @XmlAttribute
    protected String type;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public String getPlace() {
        return place;
    }

    public void setPlace(String value) {
        this.place = value;
    }

    public Date getTime() {
        return time;
    }

    public void setTime(Date value) {
        this.time = value;
    }

    public String getType() {
        return type;
    }

    public void setType(String value) {
        this.type = value;
    }
}
 


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

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

public class Pictures {
   
    private List<Picture> picture;

    public List<Picture> getPicture() {
        if (picture == null) {
            picture = new ArrayList<Picture>();
        }
        return this.picture;
    }
   
    public void setPicture(List<Picture> picture) {
        this.picture = picture;
    }
}
 

After setting up all my classes, created an unmarshaler for parsing the XML document. 


JAXBUnmarshalar
import java.io.FileInputStream;

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

public class JAXBUnmarshalar {

    public static void main(String s[]) throws Exception{
        //A JAXBContext instance is created for handling classes generated in the default package. Since Album.class is top level Java class, we could specify this class while instantiating the context instead of package
        JAXBContext jc = JAXBContext.newInstance(Album.class);
        Unmarshaller u = jc.createUnmarshaller();
       
        Album album = (Album)u.unmarshal(JAXBUnmarshalar.class.getResource("Album.xml"));
 
        /******** Unmarshalling completed at this point. Let's display the outcome *************/
 
        Marshaller jaxbMarshaller = jc.createMarshaller();
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        jaxbMarshaller.marshal(album, System.out);
    }
}

Executing the unmarshaler produced below output.

Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Album/>

Our unmarshaler failed to read all the sub element of <Album> and this output was not as per our expectation. I assumed that attribute names in our Java classes will map to XML element in spite of differences in their case (i.e. private String name; will map to XML element <Name>) and that assumption was incorrect. I didn't wanted to change attribute names of my Java classes, that was too much of work. After some googling, I reached at this link in JAXB tutorial. 

Updated all my class attribute with @XmlElement annotation mentioning corresponding XML element name. After modification, my class definition changed to 


 Album.java

@XmlRootElement(name="Album")
public class Album {
  
    @XmlElement(name="Name")
    private String name;
  
    @XmlElement(name="Date")
    private Date date;
  
    @XmlElement(name="Pictures")
    private Pictures pictures;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }
…. …. …  ..//public accessor for all other ‘private’ attributes goes here
}

After compiling the Java classes, when I executed JAXBUnmarshalar, got another set of errors:

Class has two properties of the same name "name"
    this problem is related to the following location:
        at public java.lang.String Album.getName()
        at Album
    this problem is related to the following location:
        at private java.lang.String Album.name
        at Album
 


Time for another set of annotations. See this link or below excerpts from JAXB tutorial.

 6.2.5 Controlling Element Selection: XmlAccessorType, XmlTransient
If JAXB binds a class to XML, then, by default, all public members will be bound, i.e., public getter and setter pairs, or public fields. Any protected, package-visible or private member is bound if it is annotated with a suitable annotation such as XmlElement or XmlAttribute. You have several possibilities to influence this default behaviour.

In our case 'name' attribute is bound to to JXB through 'public' accessor methods 

public String get/setName(); and  

@XmlElement
private String name; 

We can solve this problem in two ways, either place the @XmlElement annotation on accessor method or declare @XmlAccessorType annotation on top of the class.

I opted for 2nd option and annotated my class with @XmlAccessorType(XmlAccessType.FIELD)

Resulting class definition was same as previous except for addition of a new annotation.

@XmlRootElement(name="Album")
@XmlAccessorType(XmlAccessType.FIELD)
public class Album {
   
    @XmlElement(name="Name")
    private String name;
   
    @XmlElement(name="Date")
    private Date date;
   
    @XmlElement(name="Pictures")
    private Pictures pictures;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

…. …. …  ..//public accessor for all other ‘private’ attributes goes here
}

At this point, after executing my JAXBMarshaler, output XML was close to our original document:

output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Album>
    <Name>My trip to Hawai</Name>
    <Pictures>
        <Picture type="JPEG">
            <Name>Foodoo</Name>
            <Place>At Beach</Place>
        </Picture>
        <Picture type="GIF">
            <Name>Dilloo</Name>
            <Place>In hotel</Place>
        </Picture>
    </Pictures>
</Album>
The <time> element was still missing and it seems JAXB failed to parse that field in spite of everything looked good.

After some googling, I realized that JAXB didn't like date format specified in my XML document
<Time>11/01/2013 07:50:56 PM</Time> <!- MM/dd/yyyy hh:mm:ss-->

Thanks to this stakoverflow post, that indicates JAXB likes date values in this (
"yyyy-MM-dd'T'HH:mm:ss") format.

If we like to make JAXB understand our format, we need to provide some more information by using '@XmlJavaTypeAdapter' annotation. This annotation is defined as 'For some Java container types JAXB has no built-in mapping to an XML structure. Also, you may want to represent Java types in a way that is entirely different from what JAXB is apt to do. Such mappings require an adapter class, written as an extension of XmlAdapter<XmlType,ApplType> from the package javax.xml.bind.annotation.adapters.' See more about this annotation here.

I modified my 'Picture' class to have this additional information.

    @XmlJavaTypeAdapter(DateAdapter.class)
    @XmlElement(name="Time")
    private Date time;

This completed our unmarshalling task, see updated class definitions below.

Album.java
import java.util.Date;

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

@XmlRootElement(name="Album")
@XmlAccessorType(XmlAccessType.FIELD)
public class Album {
   
    @XmlElement(name="Name")
    private String name;
   
    @XmlElement(name="Date")
    private Date date;
   
    @XmlElement(name="Pictures")
    private Pictures pictures;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date value) {
        this.date = value;
    }

    public Pictures getPictures() {
        return pictures;
    }

    public void setPictures(Pictures value) {
        this.pictures = value;
    }
}
 
Picture.java
import java.util.Date;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlAccessorType(XmlAccessType.FIELD)
public class Picture {

    @XmlElement(name="Name")
    private String name;
   
    @XmlElement(name="Place")
    private String place;
   
    @XmlJavaTypeAdapter(DateAdapter.class)
    @XmlElement(name="Time")
    private Date time;

    @XmlAttribute
    protected String type;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public String getPlace() {
        return place;
    }

    public void setPlace(String value) {
        this.place = value;
    }

    public Date getTime() {
        return time;
    }

    public void setTime(Date value) {
        this.time = value;
    }

    public String getType() {
        return type;
    }

    public void setType(String value) {
        this.type = value;
    }
}

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

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

@XmlAccessorType(XmlAccessType.FIELD)
public class Pictures {
   
    @XmlElement(name="Picture")
    private List<Picture> picture;

    public List<Picture> getPicture() {
        if (picture == null) {
            picture = new ArrayList<Picture>();
        }
        return this.picture;
    }
   
    public void setPicture(List<Picture> picture) {
        this.picture = picture;
    }
}
 
DateAdaptor.java
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<String, Date> {

    private SimpleDateFormat dateFormat = new SimpleDateFormat(
            "MM/dd/yyyy HH:mm:ss");

    @Override
    public String marshal(Date v) throws Exception {
        return dateFormat.format(v);
    }

    @Override
    public Date unmarshal(String v) throws Exception {
        return dateFormat.parse(v);
    }

}

JAXBUnmarshalar
import java.io.FileInputStream;

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

public class JAXBUnmarshalar {

    public static void main(String s[]) throws Exception{
        //A JAXBContext instance is created for handling classes generated in the default package. Since Album.class is top level Java class, we could specify this class while instantiating the context instead of package
        JAXBContext jc = JAXBContext.newInstance(Album.class);
        Unmarshaller u = jc.createUnmarshaller();
       
        Album album = (Album)u.unmarshal(JAXBUnmarshalar.class.getResource("Album.xml"));
 
        /******** Unmarshalling completed at this point. Let's display the outcome *************/
 
        Marshaller jaxbMarshaller = jc.createMarshaller();
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        jaxbMarshaller.marshal(album, System.out);
    }
}
 

Executing JAXBUnmarshalar produced below output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Album>
    <Name>My trip to Hawai</Name>
    <Pictures>
        <Picture type="JPEG">
            <Name>Foodoo</Name>
            <Place>At Beach</Place>
            <Time>11/01/2013 11:15:39</Time>
        </Picture>
        <Picture type="GIF">
            <Name>Dilloo</Name>
            <Place>In hotel</Place>
            <Time>11/01/2013 07:50:56</Time>
        </Picture>
    </Pictures>
</Album>

Overall it was little difficult to start with unmarshalling, however by using some of the simple annotations it become very easy to parse the documents. 

References:

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: