How to convert a POJO to XML with JAXB

Filed under: Java, TDD, — Tags: JUnit, POJO, dom, jaxb, xml, xpath — Thomas Sundberg — 2010-01-19

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 as well. 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



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS