Getting Started with JAXB

There are quite a few APIs out there for processing XML in Java like DOM Parser, SAX Parser, SAX parser to name a few. But there are scenarios and requirements where we have our data in Java beans and we want that data to be transformed into XML or we have some data in XML and we want that to be populated into Java beans. In such scenarios we can still make use of DOM parser or SAX parser or SAX parser but the code written will be more involved. In order to cater to such requirements a new standard was introduced in Java called JAXB which stands for Java Architecture for XML binding. JAXB supports binding Java objects into XML data and XML data into Java objects. As JAXB is a specification there are many implementations of this specification and I am using the one which comes with Java SE reference implementation. (Yes, JAXB is part of Java SE distribution from Java 6.0).

Simple JAXB Tutorial with Example

To show how one can leverage JAXB I have considered a simple example which covers quite a few concepts of JAXB. Let us model the following scenario: A book has a title, list of authors, a publisher, year of publication, number of pages, cost of the book, ISBN, type of binding and list of reviews. Each review consists of the content, the name of the author who posted it, the rating given by them for the book and the date on which this was posted.

Class Diagrams for the Example

The below are the UML Class diagrams for the different classes identified for our example:

Class diagram for Book class
JAXB Class Diagram

Class diagram for Review class
JAXB Class Diagram1

Class diagram for Books class
Books class

Class diagram for Binding Enum
Binding Enum

Class diagram for Rating Enum
Rating enum

And the class diagram showing the relationship between the classes is:
Class diagram

Now that the classes have been identified, let me give some overview of JAXB API and the annotations it supports.

Overview of JAXB

JAXB provides a mechanism to convert the data in Java objects into XML which is called marshalling and from XML into Java objects which is called Unmarshalling. It requires a XML Schema definition (XSD) to facilitate the generation of XML and validation of XML to generate Java objects. This is taken care by the tools provided by the JAXB and the annotations declared on the java beans. Lets look at the different annotations supported which are used in this example:

AnnotationDescription
@XmlElementMaps a non static and non transient attribute or a getter to an XML element.
@XmlEnumMaps an Enum to an XML element
@XmlEnumValueProvides a value for Enum to be used in the XML.
@XmlRootElementThis annotation is used for any class which would become the root XML Element

There are quite a few other JAXB annotations, but in this example I will be using the above annotations only. There is much detailed tutorial on how the complete JAXB schema generation works, how these annotations get mapped to XML Schema and other advanced aspects of JAXB.

Defining the Classes

Now lets define the classes and annotate them with corresponding JAXB annotations. There can be 2 possible root elements for the XML we are going to generate- book or books. The latter is the root when there are multiple book entries in the XML and the former is the root when we are showing the XML for single book.

Review class

Lets look at the definition for our Review class. The review class is used to model the user comments on a given book and contains the content of the comment, rating given and the date on which it was posted.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.annotation.XmlElement;

/**
 * Represents the user comment on some item.
 * This is not annotated as @XMLRootElement as we dont expect this to be used
 * as a root of XML.
 */
public class Review {
  public String content;
  public Rating rating;
  public String author;
  private Date postedOn;

  public Review(String content, String author, Date postedOn, Rating rating) {
    this.content = content;
    this.rating = rating;
    this.author = author;
    this.postedOn = postedOn;
  }

  /**
   * Returns the date formatted using SimpleDateFormat and is used by JAXB
   * while generating the XML.
   */
  @XmlElement(name="postedOn")
  public String getPostedOn(){
    SimpleDateFormat sdf = new SimpleDateFormat("EEE, MMM d, ''yy");
    return sdf.format(postedOn);
  }

  /**
   * Reformats the date represented in String into a Date instance. This is used
   * while JAXB is unmarshalling the XML into Java objects.
   */
  public void setPostedOn(String posted) {
    SimpleDateFormat sdf = new SimpleDateFormat("EEE, MMM d, ''yy");
    try {
      this.postedOn = sdf.parse(posted);
    } catch (ParseException ex) {
      Logger.getLogger(Review.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  public Review() {
  }
}

Rating Enum

We use an Enum to provide different rating levels which a user can choose while rating any item. The below code explains the Rating enum;


import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;

/**
 * Represents the different levels of rating which a user can give to
 * any item.
 */
@XmlEnum(Integer.class)
public enum Rating {
  @XmlEnumValue("1") ONE(1),
  @XmlEnumValue("2") TWO(2),
  @XmlEnumValue("3") THREE(3),
  @XmlEnumValue("4") FOUR(4),
  @XmlEnumValue("5") FIVE(5);

  private Rating(int code) {
    this.code = code;
  }
  public int code;
}

Book class

The Book class holds the data associated with a book and its class definition is:

import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * Represents the data for a Book. Annotated for use with JAXB.
 */
@XmlRootElement
public class Book {

  public String title;

  @XmlElementWrapper(name="authors")
  @XmlElement(name="author")
  public List<String> authors;

  public String publisher;
  public Integer year;
  public Integer numberOfPages;
  public Double price;

  @XmlElementWrapper(name = "reviews")
  @XmlElement(name="review")
  public List<Review> reviews;

  public String isbn;
  public Binding binding;

  @Override
  public String toString(){
    return title;
  }
}

You can see that I have used @XmlElementWrapper annotation. This annotation helps in wrapping a list of child elements into a common parent element. For example in the above example I want all the review XML nodes to be part of the parent reviews XML node.

Binding Enum

Each book can either be hardbound or paperback and this is modeled using an Enum as shown below:

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;

/**
 * Represents the type of binding of the book.
 */
@XmlEnum(String.class)
public enum Binding {
  @XmlEnumValue("Paperback") PAPERBACK ("Paperback"),
  @XmlEnumValue("Hardcover") HARDCOVER ("Hardcover");

  private Binding(String displayString) {
    this.displayString = displayString;
  }

  public String displayString;
}

Books class

The Books is used to hold multiple book objects. Its just a container for multiple books as show in the code below:


import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * Container for multiple books
 */
@XmlRootElement
public class Books{

  @XmlElement(name="book")
  public List<Book> myBooks = new ArrayList<>();

  public void addToBooks(Book book){
    myBooks.add(book);
  }
  public void setBooks(List<Book> bks){
    myBooks.addAll(bks);
  }
}

Marshalling and Unmarshalling XML Data

Now that we have seen all the class definitions let us now see how one can perform marshalling and unmarshalling of XML data. Before that we need to have some data i.e some populated instances of Book class and the below class does the population of test data:

import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import model.Binding;
import model.Book;
import model.BookStock;
import model.Books;
import model.Rating;
import model.Review;

/**
 * Class for populating test data.
 */
public class BookDb {

  Books books;
  Map<String, Book> bookIsbnMap;
  Map<Book, BookStock> bookStock;

  public static BookDb bookDb = new BookDb();

  private BookDb(){

    Calendar calendar = Calendar.getInstance();
    Date today = calendar.getTime();
    calendar.add(Calendar.DATE, -4);
    Date olderDay1  =  calendar.getTime();
    calendar.add(Calendar.DATE, -10);
    Date olderDay2 = calendar.getTime();

    books = new Books();
    bookIsbnMap = new HashMap<>();
    Book book = new Book();
    book.authors = Arrays.asList("Narasimha Karumanchi");
    book.binding = Binding.PAPERBACK;
    book.isbn = "9780615459813";
    book.numberOfPages = 429;
    book.price = 344.0;
    book.publisher = "CareerMonk Publications";
    book.title = "Data Structures and Algorithms Made Easy 2nd Edition";
    book.year = 2011;
    book.reviews = (Arrays.asList(new Review("Great book", "Abc Xyz", today ,Rating.FOUR),
            new Review("Needs improvement", "Xyz Pqr", olderDay1, Rating.TWO)));

    books.addToBooks(book);
    bookIsbnMap.put(book.isbn, book);

    book = new Book();
    book.title = "Java : The Complete Reference 8 Edition";
    book.price = 438.0;
    book.authors = Arrays.asList("Herbert Schildt");
    book.binding = Binding.PAPERBACK;
    book.isbn = "9781259002465";
    book.numberOfPages = 1152;
    book.publisher = "Tata McGraw - Hill Education";
    book.year = 2011;
    book.reviews = (Arrays.asList(new Review("Great Book!!", "Pqr Wxy", today, Rating.FIVE),
            new Review("Must read for Java programmers", "Xyz Abc", olderDay1, Rating.FIVE)));

    books.addToBooks(book);
    bookIsbnMap.put(book.isbn, book);

  }

  /**
   * Adds the new instance of Book to the database i.e the List of books.
   */
  public boolean addToBookDb(Book book){
    books.addToBooks(book);
    bookIsbnMap.put(book.isbn, book);

    return true;
  }

  /**
   * API to obtain the book details given the ISBN number.
   */
  public Book getBookByIsbn(String isbn){
    return bookIsbnMap.get(isbn);
  }

  /**
   * Returns all the book details stored in the list.
   */
  public Books getAllBooks(){
    return books;
  }
}

Marshalling one instance of Book class

In the below code we marshall one instance of Book class into XML data:

public class JaxbTest {

  static BookDb bookDb;
  public JaxbTest() {
  }

  @BeforeClass
  public static void setup(){
    bookDb = BookDb.bookDb;
  }

  /**
   * Marshalling one instance of Book class.
   */
  @Test public void marshalOne() throws JAXBException{
    JAXBContext context = JAXBContext.newInstance(Book.class);
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.marshal(bookDb.getAllBooks().myBooks.get(0), System.out);

  }
}

And the output is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book>
  <title>Data Structures and Algorithms Made Easy 2nd Edition</title>
  <authors>
    <author>Narasimha Karumanchi</author>
  </authors>
  <publisher>CareerMonk Publications</publisher>
  <year>2011</year>
  <numberOfPages>429</numberOfPages>
  <price>344.0</price>
  <reviews>
    <review>
      <content>Great book</content>
      <rating>4</rating>
      <author>Abc Xyz</author>
      <postedOn>Thu, Nov 21, '13</postedOn>
    </review>
    <review>
      <content>Needs improvement</content>
      <rating>2</rating>
      <author>Xyz Pqr</author>
      <postedOn>Sun, Nov 17, '13</postedOn>
    </review>
  </reviews>
  <isbn>9780615459813</isbn>
  <binding>Paperback</binding>
</book>

Marshalling list of instances of Book class

Above we saw how to marshal one instance of Book class. In this we will see how to marshall a list of Book instances i.e instance of Books class.

/**
 * Unit test to test the Marshalling and Unmarshalling of the
 * XML using JAXB APIs.
 */
public class JaxbTest {

  static BookDb bookDb;
  public JaxbTest() {
  }

  @BeforeClass
  public static void setup(){
    bookDb = BookDb.bookDb;
  }

  /**
   * Marshalling list of books i.e instance of Books class.
   */
  @Test public void marshalAll() throws JAXBException{
    JAXBContext context = JAXBContext.newInstance(Books.class);
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.marshal(bookDb.getAllBooks(), System.out);

  }
}

And the XML generated for the above marshalling will be:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books>
  <book>
    <title>Data Structures and Algorithms Made Easy 2nd Edition</title>
    <authors>
      <author>Narasimha Karumanchi</author>
    </authors>
    <publisher>CareerMonk Publications</publisher>
    <year>2011</year>
    <numberOfPages>429</numberOfPages>
    <price>344.0</price>
    <reviews>
      <review>
        <content>Great book</content>
        <rating>4</rating>
        <author>Abc Xyz</author>
        <postedOn>Thu, Nov 21, '13</postedOn>
      </review>
      <review>
        <content>Needs improvement</content>
        <rating>2</rating>
        <author>Xyz Pqr</author>
        <postedOn>Sun, Nov 17, '13</postedOn>
      </review>
    </reviews>
    <isbn>9780615459813</isbn>
    <binding>Paperback</binding>
  </book>
  <book>
    <title>Java : The Complete Reference 8 Edition</title>
    <authors>
      <author>Herbert Schildt</author>
    </authors>
    <publisher>Tata McGraw - Hill Education</publisher>
    <year>2011</year>
    <numberOfPages>1152</numberOfPages>
    <price>438.0</price>
    <reviews>
      <review>
        <content>Great Book!!</content>
        <rating>5</rating>
        <author>Pqr Wxy</author>
        <postedOn>Thu, Nov 21, '13</postedOn>
      </review>
      <review>
        <content>Must read for Java programmers</content>
        <rating>5</rating>
        <author>Xyz Abc</author>
        <postedOn>Sun, Nov 17, '13</postedOn>
      </review>
    </reviews>
    <isbn>9781259002465</isbn>
    <binding>Paperback</binding>
  </book>
</books>

Unmarshalling the XML into Java objects

Now lets look at how we can unmarshall the below XML into Java objects.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books>
  <book>
    <title>Cracking The Coding Interview: 150 Programming Questions And Solutions </title>
    <authors>
      <author>Gayle Laakmann McDowell</author>
    </authors>
    <publisher>Pothi.com</publisher>
    <year>2011</year>
    <numberOfPages>508</numberOfPages>
    <price>504</price>
    <reviews>
      <review>
        <content>Great book</content>
        <rating>4</rating>
        <author>Abc Xyz</author>
        <postedOn>Mon, Nov 18, '13</postedOn>
      </review>
      <review>
        <content>Needs improvement</content>
        <rating>2</rating>
        <author>Xyz Pqr</author>
        <postedOn>Thu, Nov 14, '13</postedOn>
      </review>
    </reviews>
    <isbn>9780984782802</isbn>
    <binding>Paperback</binding>
  </book>
  <book>
    <title>Java : The Complete Reference 8 Edition</title>
    <authors>
      <author>Herbert Schildt</author>
    </authors>
    <publisher>Tata McGraw - Hill Education</publisher>
    <year>2011</year>
    <numberOfPages>1152</numberOfPages>
    <price>438.0</price>
    <reviews>
      <review>
        <content>Great Book!!</content>
        <rating>5</rating>
        <author>Pqr Wxy</author>
        <postedOn>Mon, Nov 18, '13</postedOn>
      </review>
      <review>
        <content>Must read for Java programmers</content>
        <rating>5</rating>
        <author>Xyz Abc</author>
        <postedOn>Thu, Nov 14, '13</postedOn>
      </review>
    </reviews>
    <isbn>9781259002465</isbn>
    <binding>Paperback</binding>
  </book>
</books>

The code which unmarshalls the above XML into Java objects and prints the data to the console:

/**
 * Unit test to test the Marshalling and Unmarshalling of the
 * XML using JAXB APIs.
 */
public class JaxbTest {

  static BookDb bookDb;
  public JaxbTest() {
  }

  @BeforeClass
  public static void setup(){
    bookDb = BookDb.bookDb;
  }

  /**
   * Unmarshalling the XML file into an instance of Books class.
   */
  @Test public void unMarshalAll() throws JAXBException{
    JAXBContext context = JAXBContext.newInstance(Books.class);
    Unmarshaller um = context.createUnmarshaller();
    Books books = (Books)um.unmarshal(new File("D:\\dev\\my_books.xml"));
    for ( Book book : books.myBooks){
      System.out.println("Details for: "+book.title);
      System.out.println("Author: "+ joinList(book.authors));
      System.out.println("ISBN: "+ book.isbn);
      System.out.println("Publisher: "+ book.publisher);
      System.out.println("Cost: "+book.price);
      System.out.println("Publication year: "+book.year);
      System.out.println("Reviews: ");
      for ( Review review : book.reviews){
        System.out.println(review.content+" by "+ review.author+" posted on "+review.getPostedOn());
      }
    }
  }

  private String joinList(List<String> list){
    StringBuilder builder = new StringBuilder();
    for ( String str : list){
      builder.append(str).append(",");
    }
    return builder.toString().substring(0, builder.length()-1);
  }
}

The output:

Details for: Cracking The Coding Interview: 150 Programming Questions And Solutions
Author: Gayle Laakmann McDowell
ISBN: 9780984782802
Publisher: Pothi.com
Cost: 504.0
Publication year: 2011
Reviews:
Great book by Abc Xyz posted on Mon, Nov 18, '13
Needs improvement by Xyz Pqr posted on Thu, Nov 14, '13
Details for: Java : The Complete Reference 8 Edition
Author: Herbert Schildt
ISBN: 9781259002465
Publisher: Tata McGraw - Hill Education
Cost: 438.0
Publication year: 2011
Reviews:
Great Book!! by Pqr Wxy posted on Mon, Nov 18, '13
Must read for Java programmers by Xyz Abc posted on Thu, Nov 14, '13

The complete code for the JUnit test which tests the Marshalling and Unmarshalling features is:

import db.BookDb;
import java.io.File;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import model.Book;
import model.Books;
import model.Review;
import org.junit.Test;
import org.junit.BeforeClass;

/**
 * Unit test to test the Marshalling and Unmarshalling of the
 * XML using JAXB APIs.
 */
public class JaxbTest {

  static BookDb bookDb;
  public JaxbTest() {
  }

  @BeforeClass
  public static void setup(){
    bookDb = BookDb.bookDb;
  }

  /**
   * Marshalling one instance of Book class.
   */
  @Test public void marshalOne() throws JAXBException{
    JAXBContext context = JAXBContext.newInstance(Book.class);
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.marshal(bookDb.getAllBooks().myBooks.get(0), System.out);

  }

  /**
   * Marshalling list of books i.e instance of Books class.
   */
  @Test public void marshalAll() throws JAXBException{
    JAXBContext context = JAXBContext.newInstance(Books.class);
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.marshal(bookDb.getAllBooks(), System.out);

  }

  /**
   * Unmarshalling the XML file into an instance of Books class.
   */
  @Test public void unMarshalAll() throws JAXBException{
    JAXBContext context = JAXBContext.newInstance(Books.class);
    Unmarshaller um = context.createUnmarshaller();
    Books books = (Books)um.unmarshal(new File("D:\\dev\\my_books.xml"));
    for ( Book book : books.myBooks){
      System.out.println("Details for: "+book.title);
      System.out.println("Author: "+ joinList(book.authors));
      System.out.println("ISBN: "+ book.isbn);
      System.out.println("Publisher: "+ book.publisher);
      System.out.println("Cost: "+book.price);
      System.out.println("Publication year: "+book.year);
      System.out.println("Reviews: ");
      for ( Review review : book.reviews){
        System.out.println(review.content+" by "+ review.author+" posted on "+review.getPostedOn());
      }
    }
  }

  private String joinList(List<String> list){
    StringBuilder builder = new StringBuilder();
    for ( String str : list){
      builder.append(str).append(",");
    }
    return builder.toString().substring(0, builder.length()-1);
  }
}

How To Convert Properties File Into XML File Using Java?

If you have any issues in following and implementing the above example feel free to drop in your issues as comments. I will try to address them at the earliest.

Comments

comments

About Mohamed Sanaulla

In his day job he works on developing enterprise applications using ADF. He is also the moderator of JavaRanch forums and an avid blogger.

Comments

  1. Mallik says:

    very useful article.

Speak Your Mind

*