Monday, May 15, 2023

JAXB Date Format Example using Annotation | Java Date to XML DateTime String Conversion

One of the common problem while marshaling Java object to XML String using JAXB is the default format of date and time provided by JAXB. When JAXB converts any Date type object or XMLGregorianCalendar to XML String, precisely xsd:dateTime element, it by default prints unformatted date e.g. 2012-05-17T09:20:00-04:30. Since most of the real world, Java application has a requirement to print date in a particular format like dd-MM-yyyy or include date and time in format dd-MM-yyyy HH:mm:ss, it becomes a problem. Thankfully, JAXB is very extensible and provides hooks and adapters to customize marshaling and unmarshaling process. You can define an extension of XmlAdapter to control and customize marshaling and unmarshaling or any Java type depending upon yours need.

In this JAXB tutorial, we will see an example of customizing JAXB to format date and time in an application specific format.

For our purpose, we will use the SimpleDateFormat class, which has its own issues, but that's ok for demonstration purpose. To get a clarity of what is the issue with the date format, and what we are doing in this example, consider following xml snippet, which is created from our Employee object, without formatting Date, XML will look like:

<employee>
<name>John</name>
<dateofbirth>1985-01-15T18:30:00-04:00</dateofbirth>
<dateofjoining>2012-05-17T09:20:00-04:30</dateofjoining>
</employee>


While after using XmlAdapter for controlling marshaling of date object in JAXB, our XML String snippet will look like below:

XML String after formatting date in dd-MM-yyyy HH:mm:ss format

<employee>
 <name>John</name>
 <dateofbirth>15-01-1985 18:30:00</dateofbirth>
 <dateofjoining>17-05-2012 09:20:00</dateofjoining>
</employee>

You can see that, in the second XML, dates are properly formatted including both date and time information. You can even further customize it into several other date formats e.g. MM/dd/yyyy, you just need to remember SimpleDateFormat syntax for formatting dates, as shown here.




JAXB Date Format Example

Here is the complete code example of formatting dates in JAXB. It allows you to specify Adapters, which can be used for marshaling and unmarshaling different types of object, for example, you can specify DateTimeAdapter for converting Date to String during marshaling, and XML String to Date during unmarshaling.

Apart from writing your own Adapter, which should extend the XmlAdapter class, you also need to use annotation @XmlJavaTypeAdapter to specify that JAXB should use that adapter for marshaling and unmarshalling of a particular object.

In this JAXB tutorial, we have written our own DateTimeAdapter, which extends XmlAdapter and overrides marshal(Date object) and unmarshal(String xml) methods. JAXB calls marshal method, while converting Java Object to XML document, and unmarshal method to bind XML document to Java object.

We are also using SimpleDateFormat class, to format Date object into dd-MM-yyyy HH:mm:ss format, which print date as 15-01-1985 18:30:00. By the way, be careful while using SimpleDateFormat, as it's not thread-safe.




Java Program to convert Java Object to XML with formatted Dates

import java.io.StringWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

/**
 *
 * JAXB tutorial to format Date to XML String while converting Java object to
 * XML documents i.e. during marshalling.
 *
 * @author Javin Paul
 */
public class JAXBDateFormatTutorial {

    public static void main(String args[]) {
   
       Date dob = new GregorianCalendar(1985,Calendar.JANUARY, 15, 18, 30)
                        .getTime();
       Date doj = new GregorianCalendar(2012,Calendar.MAY, 17, 9, 20)
                        .getTime();  
       
       Employee john = new Employee("John", dob, doj);
 
       // Marshaling Employee object to XML using JAXB
       JAXBContext ctx = null;
       StringWriter writer = new StringWriter();
 
       try{
           ctx = JAXBContext.newInstance(Employee.class);
           ctx.createMarshaller().marshal(john, writer);
           System.out.println("Employee object as XML");
           System.out.println(writer);
     
       }catch(JAXBException ex){
           ex.printStackTrace();
       }
    }

}

When you will run this program it will print the Employee object in XML as shown below:

Employee object as XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<name>John</name>
<dateOfBirth>15-01-1985 18:30:00</dateOfBirth>
<dateOfJoining>17-05-2012 09:20:00</dateOfJoining>
</employee>

You can see that dates are nicely formatted and there is no more T in between as it was before.

In this program, there are three main classes: Employee, DateTimeAdapter, and JAXBDateFormatTutorial, each class is coded in their respective file because they are public classes e.g. Employee.java contains Employee class. If you want to include then in the same file, just remove the public modifier from Employee and DateTimeAdapter class.

The Employee class is your domain object while DateTimeAdapter is an extension of XmlAdapter to format dates while converting XML to Java object and vice-versa. 

As I said, JAXB is very extensible and provides a lot of hooks where you can insert your own code for customization. If you want to learn more about the advanced usage of JAXB, I suggest you joining these Java Programing and Development courses and books to learn XML parsing in Java.

JAXB Date Format Example using Annotation





Employee.java

@XmlRootElement(name="employee")
@XmlAccessorType(XmlAccessType.FIELD)  
public class Employee{
    @XmlElement(name="name")
    private String name;

    @XmlElement(name="dateOfBirth")
        @XmlJavaTypeAdapter(DateTimeAdapter.class)
    private Date dateOfBirth;

    @XmlElement(name="dateOfJoining")
    @XmlJavaTypeAdapter(DateTimeAdapter.class)
    private Date dateOfJoining;

    // no-arg default constructor for JAXB
    public Employee(){}

    public Employee(String name, Date dateOfBirth, Date dateOfJoining) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
        this.dateOfJoining = dateOfJoining;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    public Date getDateOfJoining() {
        return dateOfJoining;
    }

    public void setDateOfJoining(Date dateOfJoining) {
        this.dateOfJoining = dateOfJoining;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Employee{" + "name=" + name + ", dateOfBirth=" 
               + dateOfBirth + ", dateOfJoining=" + dateOfJoining + '}';

    }

}

DateTimeAdapter.java

public class DateTimeAdapter extends XmlAdapter<String, Date>{
    private final DateFormat dateFormat 
               = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

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

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

}


Things to Remember

Here are a couple of important things to remember while

1) Don't forget to provide a no argument default constructor for your domain object e.g. Employee, failing to do will result in the following error while marshaling Java Object to XML String:

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions Employee does not have a no-arg default constructor.
this problem is related to the following location:

at Employee
at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:436)

at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:277)

That's all on How to format Dates in JAXB. We have not only learned Date formatting during marshaling of the Date object but also seen how to customize JAXB marshaling and unmarshalling process. You can use this technique to customize marshaling of any Java type e.g. BigDecimal, float, or double, etc. Just remember to use annotation @XmlJavaTypeAdapter to specify the name of your custom date and time Adapter to the JAXB marshaller.


Other Java XML tutorials you may like
  • What is the difference between DOM and SAX parser in Java? (answer)
  • Step by Step guide to parsing XML using SAX parser in Java? (tutorial)
  • Top 10 XML Interview Questions for Java Programmers? (FAQ)
  • How to read XML files using DOM Parser in Java? (tutorial)
  • How to escape XML Special character in Java String? (tutorial)
  • Top 10 XSLT Transformation Interview Questions? (FAQ)
  • How to parse XML documents using JDOM Parser in Java? (tutorial)
  • How to create and evaluate XPath Expressions in Java? (guide)

Thanks for reading this tutorial, if you like this tutorial then please share it with your friends and colleagues. If you have any suggestions and feedback then please share with us.

P. S. - If you are interested in learning how to deal with XML in Java in more detail, you can read Java and XML - Solutions of Real World Problem by Brett McLaughlin. It covers everything with respect to parsing XML e.g. SAX, DOM, and StAX parser, JAXB, XPath, XSLT, and JAXB. One of the good books for advanced Java developers. 

1 comment:

  1. What can I do if I don't own the POJO code (and therefore can't add a @XmlJavaTypeAdapter annotation)? Is there a way to set the default Date Format for the marshaller?

    ReplyDelete