Thomas Sundberg

January 19, 2010

How to convert a POJO to XML with JAXB

Filed under: Java — Tags: , , , , , , , — Thomas Sundberg @ 15:43

We want to convert a POJO, Plain Old Java Object, to xml and we don’t want to alter the POJO in
any significant way. How can this be done?

One solution is to annotate the POJO and use jaxb to transform it to xml.
Let’s start with a simple POJO, Car.java.

The non annotated version looks like this:

package com.agical.educational.model;

public class Car {
    private String registration;
    private String brand;
    private String description;

    public Car() {
    }

    public Car(String registration, String brand, String description) {
        this.registration = registration;
        this.brand = brand;
        this.description = description;
    }

    public String getRegistration() {
        return registration;
    }

    public void setRegistration(String registration) {
        this.registration = registration;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

And our goal is to create a xml document that may look like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<car registration="abc123">
    <brand>Volvo</brand>
    <description>Sedan</description>
</car>

And we want to do it test driven and build it using Maven.

Let’s start with a pom.xml that will solve our need for tools:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.agical.educational</groupId>
    <version>1.0</version>
    <artifactId>jaxb</artifactId>
    <name>jaxb lab</name>
    <packaging>jar</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.5</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-idea-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <downloadJavadocs>true</downloadJavadocs>
                    <downloadSources>true</downloadSources>
                    <jdkLevel>1.6</jdkLevel>
                    <jdkName>1.6</jdkName>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2</version>
        </dependency>
    </dependencies>
</project>

Let’s continue with a small test that will verify that we get a proper xml for a car.

package com.agical.educational.model;

import com.agical.educational.model.Car;
import com.agical.educational.util.XmlUtil;
import org.junit.Test;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class CarTest {

    @Test
    public void getCarAsXml() {
        String registration = "abc123";
        String brand = "Volvo";
        String description = "Sedan";

        Car car = new Car(registration, brand, description);
        XmlUtil xmlUtil = new XmlUtil();
        String xml = xmlUtil.convertToXml(car, car.getClass());

        String xpathExpression = "/car/@registration";
        String actual = xmlUtil.extractValue(xml, xpathExpression);
        assertThat(actual, is(registration));

        xpathExpression = "/car/brand";
        actual = xmlUtil.extractValue(xml, xpathExpression);
        assertThat(actual, is(brand));

        xpathExpression = "/car/description";
        actual = xmlUtil.extractValue(xml, xpathExpression);
        assertThat(actual, is(description));
    }
}

An annotated version of Car looks like this:

package com.agical.educational.model;

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

@XmlRootElement
@XmlType(propOrder = {"brand", "description"})
public class Car {
    private String registration;
    private String brand;
    private String description;

    public Car() {
    }

    public Car(String registration, String brand, String description) {
        this.registration = registration;
        this.brand = brand;
        this.description = description;
    }

    @XmlAttribute
    public String getRegistration() {
        return registration;
    }

    public void setRegistration(String registration) {
        this.registration = registration;
    }

    @XmlElement
    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    @XmlElement
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

A utility class is needed to convert an annotated class to xml. It may look like this:

package com.agical.educational.util;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.StringWriter;

public class XmlUtil {
    public String convertToXml(Object source, Class... type) {
        String result;
        StringWriter sw = new StringWriter();
        try {
            JAXBContext carContext = JAXBContext.newInstance(type);
            Marshaller carMarshaller = carContext.createMarshaller();
            carMarshaller.marshal(source, sw);
            result = sw.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }

        return result;
    }
}

This is all that is needed to be able to annotate a class and convert it to xml using jaxb.

Additional material

I also created a utility to read a value from an xml document and the final version of XmlUtil ended up like this:

package com.agical.educational.util;

import org.w3c.dom.Document;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;

public class XmlUtil {
    public String convertToXml(Object source, Class... type) {
        String result;
        StringWriter sw = new StringWriter();
        try {
            JAXBContext carContext = JAXBContext.newInstance(type);
            Marshaller carMarshaller = carContext.createMarshaller();
            carMarshaller.marshal(source, sw);
            result = sw.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }

        return result;
    }

    public String extractValue(String xml, String xpathExpression) {
        String actual;
        try {
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            documentBuilderFactory.setNamespaceAware(true);
            documentBuilderFactory.setIgnoringElementContentWhitespace(true);
            DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder();

            byte[] bytes = xml.getBytes("UTF-8");
            InputStream inputStream = new ByteArrayInputStream(bytes);
            Document doc = docBuilder.parse(inputStream);
            XPathFactory xPathFactory = XPathFactory.newInstance();
            XPath xpath = xPathFactory.newXPath();

            actual = xpath.evaluate(xpathExpression, doc, XPathConstants.STRING).toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return actual;
    }
}

As an exercise for myself, I decided to create a collection of cars and annotate it aswell.
A Car Data Access Object stub may look like this:

package com.agical.educational.model;

import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.LinkedList;
import java.util.List;

@XmlRootElement(name = "cars")
public class CarDAO {
    @XmlAnyElement
    public List<Car> cars;

    public CarDAO() {
        cars = new LinkedList<Car>();
    }

    public void addCar(Car car) {
        cars.add(car);
    }
}

A test class that adds two cars and reads the xml back may look like this

package com.agical.educational.model;

import com.agical.educational.model.Car;
import com.agical.educational.model.CarDAO;
import com.agical.educational.util.XmlUtil;
import org.junit.Test;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class CarDAOTest {

    @Test
    public void getCarsAsXml() {
        String firstCarRegistration = "abc123";
        String secondCarRegistration = "123abc";

        Car car = new Car(firstCarRegistration, "Volvo", "Sedan");

        CarDAO carDAO = new CarDAO();
        carDAO.addCar(car);

        car = new Car(secondCarRegistration, "Opel", "Truck");
        carDAO.addCar(car);

        XmlUtil xmlUtil = new XmlUtil();
        String xml = xmlUtil.convertToXml(carDAO, carDAO.getClass(), car.getClass());

        String xpathExpression = "/cars/car/@registration";
        String actual = xmlUtil.extractValue(xml, xpathExpression);
        assertThat(actual, is(firstCarRegistration));
    }
}

Resources

  • jaxb – The tool used for annotation of a a POJO so it can be converted to XML
  • Maven – The build tool used in this example to build it
  • Thomas Sundberg – The author
About these ads

18 Comments »

  1. What is the cost of using the annotations? For Instance, if I have my POJO’s and use them as input/output for web service calls but then make the EJB accessible only through RMI, how does this effect performance? Is there still overheard with the annotations on the POJO, or should the annotations be removed. The annotations are nice to have because we can easily unmarshal our response and view it in a nice format, but I’m not sure what the overhead is for our production environment where we are obtaining ~100k calls per hour.

    Comment by Keith S. — September 9, 2011 @ 16:51

    • Hi!

      Annotations are only used by tools to do some something with a class. Annotations doesn’t add overhead per se, it is what the tool will do with the annotations that will add overhead. The only reasonable way to measure if they will add any overhead that is problematic in your situation is of course to test with and without the annotations.

      Implement the same functionality using annotated classes and run a suit of tests that simulates say 5 hours worth of traffic. The do the same without the annotated solution and test the with same load. Is the difference significant? Use the solution that is best both from a performance perspective and a maintenance perspective.

      /Thomas

      Comment by Thomas Sundberg — September 10, 2011 @ 06:18

  2. […] followed this example http://thomassundberg.wordpress.com/2010/01/19/how-to-convert-a-pojo-to-xml-with-jaxb/. and it worked for single element of BillSummary and without having BillAllocation Set in my […]

    Pingback by CCE in calling JAXBContext.newInstance while marshalling hibernate objects to XML | Gravity Layouts — October 15, 2011 @ 13:33

  3. […] to the onedb engine. This is often not possible for legacy classes or conflicts with building ‘pure’ POJO objects. The onedb engine therefore allows defining nodes in the onedb cloud, which do not […]

    Pingback by onedb Architecture and Design « Missing Link — May 6, 2012 @ 00:35

  4. how do you compare xstream library with JAXB for converting POJO to XML ?

    Comment by Meyappan — May 24, 2012 @ 14:04

    • I haven’t done that. What is you experience?

      /Thomas

      Comment by Thomas Sundberg — May 24, 2012 @ 14:35

      • i am developing a RESTful service which reads data from oracle database, and populates POJO using a framework like hibernate, and then POJO are converted to XML which will be the output from REST application which can be viewed using a browser by typing in a REST request URL. I was exploring other methods for POJO to XML conversion. it also works in a similar method to the JAXB library you have explained above. POJO are annotated with @xstreamAlias for xmlElement , @xstreamAsAttribute for xmlAttribute , and then the xstream API on the POJO will serialize them to xml output visible on the browser.

        i am still checking out where the hook or the web application configuration is specified so that the POJO is operated upon the configured xstream API’s.

        to my knowledge i am aware that the output from the Controller is a org.springframework.web.servlet.ModelAndView object which wraps the POJO which has to be serialized. hope i am making sense here.

        Comment by Meyappan — May 24, 2012 @ 15:46

  5. i also believe we can also add custom converters by extending com.thoughtworks.xstream.converters.Converter and overriding 2 methods – boolean canConvert(Class classType) and void marshal(Object value, com.thoughtworks.xstream.io.HierarchicalStreamWriter writer, com.thoughtworks.xstream.converters.MarshallingContext context) to add custom xml serialization.

    also i think there is some integration between spring framework, and xstream, and finding many spring MVC applications using xstream. for example any class extending the org.springframework.web.servlet.view.xml.MarshallingView can use the org.springframework.oxm.xstream.XStreamMarshaller as it marshaller..

    let me know if you discover any more information regarding the same..

    Comment by Meyappan — May 24, 2012 @ 19:48

  6. sorry for the digression to xstream as it is not related to the main thread here, but this topic is related to POJO to xml conversion only using other methods..

    Comment by Meyappan — May 24, 2012 @ 19:50

  7. i have 2 queries related to the context/subject of this thread
    – what changes would be needed to write the POJO object to an output stream of a socket, or a servlet output stream , or just to write it directly to a file. in my case i would want the objects to be written to the output stream of the HttpServletResponse object.

    – what does @XmlAnyElement annotation inform the tool which is marshalling the object into xml. does it mean it is a collection or list object, and the tool has to iterate over the objects in the list, and invoke the marshaller on each of the list object ?

    Comment by Meyappan — May 24, 2012 @ 21:37

  8. the other thing i wanted to know is how to add formatting to the output xml. the string xml is printed out in one line. but i would want the output xml to be formatted and indented according to the hierarchy of the data.

    Comment by Meyappan — May 25, 2012 @ 13:22

  9. for example instead of printing a string xml like VolvoSedanOpelTruck

    we need to print it with formatting like below

    Volvo
    Sedan

    Opel
    Truck

    can we add a custom Writer /PrettyPrintWriter etc to customize how we need to write the xml, for example we need to customize flat file printing with a CData Section in the xml.

    Comment by Meyappan — May 25, 2012 @ 15:23

  10. sorry the previous xml message was formatted wrongly by the editor. so enclosing the xml with double codes.
    instead of printing a one line xml string like this – “VolvoSedanOpelTruck”

    we want to print like – ”

    Volvo
    Sedan

    Opel
    Truck

    ” to the output stream of a HttpServletResponse object.

    Comment by Meyappan — May 25, 2012 @ 15:28

  11. Did you ever think about fetching and reusing the JaxbContext for the objects, which must already exist in the Jersey framework if a call came from the client as a string and then got converted to a POJO … for those of us who want to do something like get a json-string message and then proxy it forward to another webservice as a xml-string message … it would be very cool to not have to create additional jaxbcontext objects for the conversion … thoughts?

    Comment by pulkitsinghal — August 18, 2012 @ 01:39

    • The Jersey framework is not used here. So no, I did not consider doing it that way.

      /Thomas

      Comment by Thomas Sundberg — August 18, 2012 @ 08:50

  12. Nice tutorial, appreciate your effort

    Comment by umyangel — April 15, 2013 @ 23:14


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Silver is the New Black Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 60 other followers

%d bloggers like this: