Spring Object/XML mapping example

In this tutorial we will look into Spring’s Object/XML mapping support.

Spring provides an elegant way to convert Object to XML and then XML back to Object.

Marshaller and Unmarshaller

Spring abstracts all marshalling operations behind the org.springframework.oxm.Marshaller interface, which looks like this.

public interface Marshaller {
    /**
    * Marshals the object graph with the given root into the provided Result.
    */
    void marshal(Object graph, Result result)throws XmlMappingException, IOException;
    
     /**
     * Indicates whether this marshaller can marshal instances of the supplied type.
     */
     boolean supports(Class clazz);     
}

The Marshaller interface has one main method, which marshals the given object to a given javax.xml.transform.Result. Result is a tagging interface that basically represents an XML output abstraction: concrete implementations wrap various XML representations, as indicated in the table below.

Result implementation Wraps Xml representation
DOMResult org.w3c.dom.Node
SAXResult org.xml.sax.ContentHandler
StreamResult java.io.File, java.io.OutputStream, or
java.io.Writer

The Unmarshaller looks like this

public interface Unmarshaller {
    /**
    * Unmarshals the given provided Source into an object graph.
    */
    Object unmarshal(Source source)throws XmlMappingException, IOException;

    /**
    * Indicates whether this unmarshaller can unmarshal instances of the supplied type. 
    */
    boolean supports(Class clazz);
}

This interface also has one method, which reads from the given javax.xml.transform.Source (an XML input bstraction), and returns the object read. As with Result, Source is a tagging interface that has three concrete implementations. Each wraps a different XML representation, as shown in the table below.

Result implementation Wraps Xml representation
DOMSource org.w3c.dom.Node
SAXSource org.xml.sax.InputSource, and org.xml.sax.XMLReader
StreamSource java.io.File, java.io.InputStream, or
java.io.Reader

One thing to note here is that most of the Marshaller and Unmarshaller implementations can not handle arbitrary objects and need some kind of hint to understand what Java property needs to be mapped to which xml element/tag etc. These hints are provided using either a mapping xml file or using java annotations or a common base class.
Now let us quickly jump to a sample application.

Now let us quickly jump to our sample application:

Tools used

  • Spring 3.2.2
  • Castor Api
  • Eclipse
  • Java

What are we going to do here?

In our sample application we have a couple of domain objects ie. Department and Employee Class. Here department has a list of employees. We will Marshall it into xml and unmarshall it to Java again.

Project Structure

spring-xml-marshaling

Maven Dependencies

pom.xml

<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.bharatonjava</groupId>
	<artifactId>xml-marshelling-spring</artifactId>
	<packaging>jar</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>xml-marshelling-spring Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<properties>
		<spring.version>3.2.2.RELEASE</spring.version>
	</properties>

	<dependencies>

		<!-- Spring 3 dependencies -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- spring oxm -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Uses Castor for XML -->
		<dependency>
			<groupId>org.codehaus.castor</groupId>
			<artifactId>castor</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Castor need this -->
		<dependency>
			<groupId>xerces</groupId>
			<artifactId>xercesImpl</artifactId>
			<version>2.8.1</version>
		</dependency>

	</dependencies>

	<build>
		<finalName>xml-marshelling-spring</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>

	</build>
</project>

Domain Objects


/**
* The Department class
*/
package com.bharatonjava.domain;

import java.util.ArrayList;
import java.util.List;

public class Department {

	private String name;

	private List<Employee> employees = new ArrayList<Employee>();

	public Department() {
		// TODO Auto-generated constructor stub
	}

	public String getName() {
		return name;
	}

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

	public List<Employee> getEmployees() {
		return employees;
	}

	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}

	@Override
	public String toString() {
		return "Department [name=" + name + ", employees=" + employees + "]";
	}

}



/**
* The Employee Class
*/
package com.bharatonjava.domain;

public class Employee {

	private String name;
	private int age;
	private double salary;

	public Employee() {
	}

	public Employee(String name, int age, double salary) {
		super();
		this.name = name;
		this.age = age;
		this.salary = salary;
	}

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	@Override
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + ", salary=" + salary
				+ "]";
	}

}

XmlMarshaller.java

package com.bharatonjava.xml;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;

import com.bharatonjava.domain.Department;
import com.bharatonjava.domain.Employee;

public class XmlMarshaller {

	private static final String FILE_NAME = "department.xml";

	private Marshaller marshaller;
	private Unmarshaller unmarshaller;

	public void setMarshaller(Marshaller marshaller) {
		this.marshaller = marshaller;
	}

	public void setUnmarshaller(Unmarshaller unmarshaller) {
		this.unmarshaller = unmarshaller;
	}

	/**
	 * Java to xml
	 * @throws IOException
	 */
	public void saveDepartment() throws IOException {
		FileOutputStream os = null;
		Employee e1 = new Employee("Bharat", 30, 50000);
		Employee e2 = new Employee("Jayant", 22, 40000);

		Department d = new Department();
		d.setName("Accounts");
		d.getEmployees().add(e1);
		d.getEmployees().add(e2);

		try {
			os = new FileOutputStream(FILE_NAME);
			this.marshaller.marshal(d, new StreamResult(os));
		} finally {
			if (os != null) {
				os.close();
			}
		}
	}

	/**
	 * xml to java
	 * @throws IOException
	 */
	public void loadDepartment() throws IOException {
		FileInputStream is = null;

		Department d = null;
		try {
			is = new FileInputStream(FILE_NAME);
			d = (Department) this.unmarshaller.unmarshal(new StreamSource(is));
		} finally {
			if (is != null) {
				is.close();
			}
		}

		System.out.println(d);
	}

	/**
	 * The main method
	 * @param args
	 * @throws IOException
	 */
	
	public static void main(String[] args) throws IOException {
		ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		XmlMarshaller marsheller = (XmlMarshaller) appContext.getBean("marsheller");
		
		marsheller.saveDepartment();
		marsheller.loadDepartment();
		
		System.out.println("done.");
	}
}

Spring Configuration

In the Spring’s bean configuration file, inject CastorMarshaller as the XML binding framework.
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:oxm="http://www.springframework.org/schema/oxm"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/oxm
http://www.springframework.org/schema/oxm/spring-oxm.xsd">

	<bean id="marsheller" class="com.bharatonjava.xml.XmlMarshaller">
		<property name="marshaller" ref="castorMarshaller" />
		<property name="unmarshaller" ref="castorMarshaller" />
	</bean>

	<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller">
		<property name="mappingLocation" value="classpath:mapping.xml" />
	</bean>
	
</beans>

Mapping Configuration

The configuration xml required by castor to identify what java class property maps to which xml element/attribute.
mapping.xml

To know more about Castor xml mapping visit http://castor.codehaus.org/xml-mapping.html

<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
                         "http://castor.org/mapping.dtd">
<mapping>

	<description>
		One mapping file can contain multiple class mappings and nested relations between them
	</description>

	<class name="com.bharatonjava.domain.Department">
		
		<map-to xml="department" />
		
		<field name="name" type="string">
			<bind-xml name="name" node="attribute"/>
		</field>
		
		<field name="employees" type="com.bharatonjava.domain.Employee" collection="arraylist">
			 <bind-xml name="employee" />
		</field>
		
	</class>

	<class name="com.bharatonjava.domain.Employee">

		<map-to xml="employee"  />

		<field name="age" type="integer">
			<bind-xml name="age" node="attribute" />
		</field>

		<field name="name" type="string">
			<bind-xml name="name" node="element" />
		</field>

		<field name="salary" type="double">
			<bind-xml name="salary" node="element" />
		</field>
	</class>
</mapping>

Output

On marshaling following xml gets generated.
department.xml

<?xml version="1.0" encoding="UTF-8"?>
<department name="Accounts">
	<employee age="30">
		<name>Bharat</name>
		<salary>50000.0</salary>
	</employee>
	<employee age="22">
		<name>Jayant</name>
		<salary>40000.0</salary>
	</employee>
</department>

Console output

May 19, 2013 1:12:22 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@15a3d6b: startup date [Sun May 19 13:12:22 IST 2013]; root of context hierarchy
May 19, 2013 1:12:22 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [applicationContext.xml]
May 19, 2013 1:12:22 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1da669c: defining beans [marsheller,castorMarshaller]; root of factory hierarchy
Department [name=Accounts, employees=[Employee [name=Bharat, age=30, salary=50000.0], Employee [name=Jayant, age=22, salary=40000.0]]]
done.

Source Code

Download Sourcecode

Advertisements

, , , ,

  1. #1 by mak on June 5, 2014 - 10:20 am

    Thanks for providing this wonderful tutorial.
    I have a one question,I want to generate blank element like in following example
    true
    mkyong
    This is address
    How to generate this .
    Please help me.
    Thanks.

  2. #2 by mak on June 6, 2014 - 11:28 am

    Thanks for providing this wonderful tutorial.
    I have a one question,I want to generate blank element like in following example
    true
    mkyong
    This is address
    How to generate this .
    Please help me.
    Thanks.

  3. #3 by Muhammed Abdul Khaliq on February 25, 2016 - 7:21 pm

    Hi, Thank you for sharing. What if salary also has an attribute say currency, how do i map then?

    Both the value and the attribute?

    Please help;

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

%d bloggers like this: