Search This Blog

Friday 9 August 2013

Custom Marshaling With JAXB

Consider the below class:
@XmlRootElement
public class Person {
   private String name;
   private BigDecimal weight;
   private Date dob;

   // getters and setter
}
If we were to marshal a Person instance the generated XML would be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
   <dob>2013-06-15T13:01:35.420+05:30</dob>
   <name>Test</name>
   <weight>65.876499999999992951416061259806156158447265625</weight>
</person>
The value for dob is a little complex. Could we change this to a more suitable value ?

JAXB allows us to define how a particular java class it to be marshaled and unmarshaled:
public class DateAdapter extends XmlAdapter<String, Date> {

   private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

   public Date unmarshal(final String xml) throws Exception {
      return this.dateFormat.parse(xml);
   }

   public String marshal(final Date object) throws Exception {
      return this.dateFormat.format(object);
   }

}
The class is required to implement two methods - one to be called while marshaling and the other will be invoked as a part of the unmarshal process. Here we have removed the time component from the xml and the object.
But how do we inform the marshaler to use this class.
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
   private String name;
   private BigDecimal weight;
   @XmlJavaTypeAdapter(DateAdapter.class)
   private Date dob;

   // getters and setter
}
The XmlJavaTypeAdapter annotation tells JAXB to use the DateAdaptor class for marshaling and unmarshaling the dob field. (The XmlAccessorType(XmlAccessType.FIELD) is necessary else the code fails with error:
java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: com.sun.xml.internal.bind.v2.runtime.Ille
galAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "dob"
 this problem is related to the following location:
  at public java.util.Date test.jaxb.custom.marshaling.Person.getDob()
  at test.jaxb.custom.marshaling.Person
 this problem is related to the following location:
  at private java.util.Date test.jaxb.custom.marshaling.Person.dob
  at test.jaxb.custom.marshaling.Person

 at test.jaxb.custom.marshaling.TestAdapter.<clinit>(TestAdapter.java:18)
If we were to run the code now the XML created is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
   <name>Test</name>
   <weight>65.876499999999992951416061259806156158447265625</weight>
   <dob>2013-06-15</dob>
</person>
As seen the date is now in a specific format.
Such adapters can be defined whenever we need to manage the marshaling of classes as per our own rules. For example the BigDecimal value for weight has too many numbers after the decimal. A similar one can be added for the BigDecimal field.

No comments:

Post a Comment