Spring Module OXM – A new feature of Spring Framework 3.0
Since a few days Spring 3.0 is out. The frameworks core APIs (e.g. the BeanFactory) have been updated for Java 1.5. But there are also some new features in Spring 3.0. Today I will take a look on Springs new OXM-feature and see how it can be used. Naturally all features added to the Spring framework are easy to use. Let's see if Springs simplicity still exists...
What is OXM in Spring?
OXM is an abbreviation for Object-to-XML-Mapping. An O/X-Mapper allows you to map Java objects to XML-Data and XML-Data to Java objects. This is also known as XML-Marshalling or XML-Serialization and is absolutely no new invention. But this feature has now been added to the Spring framework. Let's see what is promised in the Spring reference:
- Easy of configuration
- Consistent Interfaces
- Consistent Exception Hierarchy
This sounds nice because we hate bad interfaces and we hate complex configurations. To test if Spring's new OXM-support lives up to its promise, I created a small Spring application. I think that nowadays every Java delevoper knows how to set up a simple Spring application. If not: There are a lot of good tutorials in the internet. This post will focus on how the OXM-feature can be used.
OXM in Spring 3.0
Download: Dowload the full example here. (Absolutely no warranty!)
To take a look at Spring's OXM-feature I created a simple Maven 2 project. First I created a pom.xml file:
- <project>
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.unitedcoders.examples</groupId>
- <artifactId>SpringOxmExample</artifactId>
- <packaging>jar</packaging>
- <version>1.0.0-SNAPSHOT</version>
- <name>SpringOxmExample</name>
- <url>http://united-coders.com</url>
- <build>
- <finalName>SpringOxmExample</finalName>
- <plugins>
- <!-- Compiler Plugin to compile on Java 1.6 -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <compilerVersion>1.6</compilerVersion>
- <fork>true</fork>
- <source>1.6</source>
- <target>1.6</target>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <!-- Properties -->
- <properties>
- <spring-version>3.0.0.RELEASE</spring-version>
- </properties>
- <!-- Dependencies -->
- <dependencies>
- <!-- Spring framework -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>${spring-version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-beans</artifactId>
- <version>${spring-version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-oxm</artifactId>
- <version>${spring-version}</version>
- </dependency>
- <!-- Castor Xml -->
- <dependency>
- <groupId>org.codehaus.castor</groupId>
- <artifactId>castor</artifactId>
- <version>1.2</version>
- </dependency>
- <dependency>
- <groupId>xerces</groupId>
- <artifactId>xercesImpl</artifactId>
- <version>2.9.1</version>
- </dependency>
- <!-- Logging -->
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.1.1</version>
- </dependency>
- <!-- Test dependencies -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.4</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
As you can see I defined my Spring-depencies on version 3.0.0.RELEASE. For my simple project I used the three Spring modules spring-core, spring-beans and spring-oxm. Additionally I defined the Castor project and Apache Xerces as my XML-Marshalling-dependencies. For some basic logging I used commons-logging and to feel a little bit test-driven I added junit 4.4.
After that I created a simple example class that should be serialized to XML later on:
- package com.unitedcoders.examples.spring.oxm.beans;
- public class Person {
- private String firstname;
- private String lastname;
- private boolean developer;
- public String getFirstname() {
- return firstname;
- }
- public void setFirstname(String firstname) {
- this.firstname = firstname;
- }
- public String getLastname() {
- return lastname;
- }
- public void setLastname(String lastname) {
- this.lastname = lastname;
- }
- public boolean isDeveloper() {
- return developer;
- }
- public void setDeveloper(boolean developer) {
- this.developer = developer;
- }
- }
Then I created an interface for my O/X-Mapper:
- package com.unitedcoders.examples.spring.oxm.mapper;
- import java.io.IOException;
- public interface OxMapper {
- /**
- * Serializes assigned Object into a file with the assigned name.
- *
- * @param object
- * - Object that should be serialized
- * @param filename
- * - name of the XML-file
- * @throws IOException
- */
- public abstract void writeObjectToXml(Object object, String filename) throws IOException;
- /**
- * Deserializes an object from the assigned file.
- *
- * @param filename
- * - name of the file that should be deserialized
- * @return deserialized object
- * @throws IOException
- */
- public abstract Object readObjectFromXml(String filename) throws IOException;
- }
I defined two methods. One to write an object into my XML-File and one to read an object from my XML-File. The implementation of the OxMapper-interface looks like this:
- package com.unitedcoders.examples.spring.oxm.mapper;
- 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.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.oxm.Marshaller;
- import org.springframework.oxm.Unmarshaller;
- import org.springframework.oxm.XmlMappingException;
- public class OxMapperImpl implements OxMapper {
- private static final Log LOG = LogFactory.getLog(OxMapperImpl.class);
- private Marshaller marshaller;
- private Unmarshaller unmarshaller;
- public void writeObjectToXml(Object object, String filename) throws IOException {
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(filename);
- marshaller.marshal(object, new StreamResult(fos));
- } catch (XmlMappingException e) {
- LOG.error("Xml-Serialization failed due to an XmlMappingException.", e);
- } catch (IOException e) {
- LOG.error("Xml-Serialization failed due to an IOException.", e);
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
- }
- public Object readObjectFromXml(String filename) throws IOException {
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(filename);
- return unmarshaller.unmarshal(new StreamSource(fis));
- } catch (IOException e) {
- LOG.error("Xml-Deserialization failed due to an IOException.", e);
- } finally {
- if (fis != null) {
- fis.close();
- }
- }
- return null;
- }
- public Marshaller getMarshaller() {
- return marshaller;
- }
- public void setMarshaller(Marshaller marshaller) {
- this.marshaller = marshaller;
- }
- public Unmarshaller getUnmarshaller() {
- return unmarshaller;
- }
- public void setUnmarshaller(Unmarshaller unmarshaller) {
- this.unmarshaller = unmarshaller;
- }
- }
Take a look at the members of this class. I used two new interfaces out of the Spring OXM module:
- org.springframework.oxm.Marshaller and
- org.springframework.oxm.Unmarshaller
As you can expect these two interfaces are used to marshall and unmarshall my objects.
But now take a look at the methods readObjectFromXml and writeObjectToXml. This is really simple, isn't it? I think it is. This is everything. Nothing more is needed to write and read objects from and to XML. But why I don't use a JAXB-Marshaller? JAXB also allows to serialize and deserialize objects. The answer is quiet easy: If I work based on the Spring OXM interfaces, I am able to change the marshaller implementation by simply changing my applications spring context. It is neccessary to change the code. Let's take a look at the applications context to see how this becomes possible:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans>
- <bean id="oxMapper" class="com.unitedcoders.examples.spring.oxm.mapper.OxMapperImpl">
- <property name="marshaller" ref="castorMarshaller" />
- <property name="unmarshaller" ref="castorMarshaller" />
- </bean>
- <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" />
- </beans>
I created a singleton CastorMarshaller as described in the Spring reference. This Marshaller implementation implements both interfaces. Therefore I can use it as my marshaller and my unmarshaller. If I now like to change my marshaller implementation, I only have to modify my applications context. To change the marschaller implementation no change of my code is needed.
Serialization and Deserialization
Let's now create a simple Main-Class to see how my OxMapper can be used:
- package com.unitedcoders.examples.spring.oxm;
- import java.io.IOException;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.unitedcoders.examples.spring.oxm.beans.Person;
- import com.unitedcoders.examples.spring.oxm.mapper.OxMapper;
- public class ExampleApp {
- /**
- * @param args
- */
- public static void main(String[] args) throws IOException {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("example-context.xml");
- OxMapper oxMapper = (OxMapper) context.getBean("oxMapper");
- // Creating a testperson
- Person person = new Person();
- person.setFirstname("Phil");
- person.setLastname("Steffensen");
- person.setDeveloper(true);
- String filename = "person.xml";
- // Serialization
- oxMapper.writeObjectToXml(person, filename);
- // Deserialization
- Person deserializedPerson = (Person) oxMapper.readObjectFromXml(filename);
- System.out.println("Firstname: " + deserializedPerson.getFirstname());
- System.out.println("Lastname : " + deserializedPerson.getLastname());
- System.out.println("Developer: " + deserializedPerson.isDeveloper());
- }
- }
After running this code you will see that you have generated a small XML-File containing your person.
- <?xml version="1.0" encoding="UTF-8"?>
- <person developer="true">
- <lastname>Steffensen</lastname>
- <firstname>Phil</firstname>
- </person>
That's all you need to know to (de)serialize simple objects from and to XML. But what do we do if a person gots a list of friends?
Marshalling nested objects
To try how to serialize nested objects I added a list of friends to my person.
- package com.unitedcoders.examples.spring.oxm.beans;
- import java.util.ArrayList;
- import java.util.List;
- public class Person {
- private String firstname = "";
- private String lastname = "";
- private boolean developer = false;
- private List<Person> friends = new ArrayList<Person>();
- public String getFirstname() {
- return firstname;
- }
- public void setFirstname(String firstname) {
- this.firstname = firstname;
- }
- public String getLastname() {
- return lastname;
- }
- public void setLastname(String lastname) {
- this.lastname = lastname;
- }
- public boolean isDeveloper() {
- return developer;
- }
- public void setDeveloper(boolean developer) {
- this.developer = developer;
- }
- public List<Person> getFriends() {
- return friends;
- }
- public void setFriends(List<Person> friends) {
- this.friends = friends;
- }
- public void addFriend(Person friend) {
- friends.add(friend);
- }
- }
And I changed my Main-Class and added some friends to my person.
- package com.unitedcoders.examples.spring.oxm;
- import java.io.IOException;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.unitedcoders.examples.spring.oxm.beans.Person;
- import com.unitedcoders.examples.spring.oxm.mapper.OxMapper;
- public class ExampleApp {
- /**
- * @param args
- */
- public static void main(String[] args) throws IOException {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
- "example-context.xml");
- OxMapper oxMapper = (OxMapper) context.getBean("oxMapper");
- Person person = new Person();
- person.setFirstname("Phil");
- person.setLastname("Steffensen");
- person.setDeveloper(true);
- Person friend1 = new Person();
- friend1.setFirstname("Christian");
- friend1.setLastname("Harms");
- friend1.setDeveloper(true);
- person.addFriend(friend1);
- Person friend2 = new Person();
- friend2.setFirstname("John");
- friend2.setLastname("Doe");
- friend2.setDeveloper(false);
- person.addFriend(friend2);
- String filename = "person_with_friends.xml";
- oxMapper.writeObjectToXml(person, filename);
- Person deserializedPerson = (Person) oxMapper.readObjectFromXml(filename);
- System.out.println("Firstname: " + deserializedPerson.getFirstname());
- System.out.println("Lastname : " + deserializedPerson.getLastname());
- System.out.println("Developer: " + deserializedPerson.isDeveloper());
- System.out.println("Friends : " + deserializedPerson.getFriends().size());
- }
- }
The result looks like this:
- <?xml version="1.0" encoding="UTF-8"?>
- <person developer="true">
- <friends xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" developer="true" xsi:type="java:com.unitedcoders.examples.spring.oxm.beans.Person">
- <lastname>Harms</lastname>
- <firstname>Christian</firstname>
- </friends>
- <friends xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" developer="false" xsi:type="java:com.unitedcoders.examples.spring.oxm.beans.Person">
- <lastname>Doe</lastname>
- <firstname>John</firstname>
- </friends>
- <lastname>Steffensen</lastname>
- <firstname>Phil</firstname>
- </person>
Nested objects are serialized easily. To show how this works by using an other marshaller i'd like to use JAXB.
Using JAXB on Spring OXM
To use my person as a JAXB bean I annotated it as a XmlRootElement as you can see here:
- package com.unitedcoders.examples.spring.oxm.beans;
- import java.util.ArrayList;
- import java.util.List;
- import javax.xml.bind.annotation.XmlRootElement;
- @XmlRootElement(name="person")
- public class Person {
- // ...
- }
After that inserted a Spring component-scan to my context. The annotated person bean can now be found by the component-scan. I removed the old Castor marshaller bean and inserted a new JAXB2 marshaller bean to my context. This JAXB marshaller now replaces the old Castor marshaller. As you see I changed my marschaller without changing the implementation of my OxMapper.
- <?xml version="1.0" encoding="UTF-8"?>
- <beans>
- <context:component-scan base-package="com.unitedcoders.examples.spring.oxm.beans" />
- <bean id="oxMapper" class="com.unitedcoders.examples.spring.oxm.mapper.OxMapperImpl">
- <property name="marshaller" ref="jaxbMarshaller" />
- <property name="unmarshaller" ref="jaxbMarshaller" />
- </bean>
- <oxm:jaxb2-marshaller id="jaxbMarshaller">
- <oxm:class-to-be-bound name="com.unitedcoders.examples.spring.oxm.beans.Person" />
- </oxm:jaxb2-marshaller>
- </beans>
The result is now marshalled by JAXB looks like this:
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- <person>
- <developer>true</developer>
- <firstname>Phil</firstname>
- <friends>
- <developer>true</developer>
- <firstname>Christian</firstname>
- <lastname>Harms</lastname>
- </friends>
- <friends>
- <developer>false</developer>
- <firstname>John</firstname>
- <lastname>Doe</lastname>
- </friends>
- <lastname>Steffensen</lastname>
- </person>
What do you think?
I think the Spring OXM module is absolutely usable. It is a nice way to keep the code independent from the underlying marshalling technology. And there are a lot more ways to use Spring OXM. At this time the Castor project, Apache XMLBeans, JiBX, XStream and JAXB is supported. You are free to choose your marschalling technology. It is absolutely nice and enriches the Spring Framework a lot. Thumbs up!
Download: Dowload the full example here. (Absolutely no warranty!)
[Introduzione] Marshalling XML Utilizzando Spring 3 OXM
from Diegoitaliait's Blog on Fri, 09/17/2010 - 13:36Spring 3 tra le molte novità che ha apportato, c’è sicuramente la semplificazione della gestione della informazioni in XML, offrendo delle nuove interfaccie che si occupano di effettuare il Marshaller & Unmarshaller, che non è altro che la se...
Social comments and analytics for this post
from uberVU - social comments on Fri, 01/01/2010 - 19:00This post was mentioned on Twitter by perishedde: Ich habe mal wieder etwas verfasst:http://tinyurl.com/y8lho5u #Fachartikel #Spring #OXM
elmaroufy's status on Friday, 01-Jan-10 17:35:44 UTC
from elmaroufy on Fri, 01/01/2010 - 18:37Spring OXM (Object/XML Mapping) – A new feature of Spring Framework 3.0 http://is.gd/5IUrg
Attachment | Size |
---|---|
SpringOxmExample.zip_.bz2 | 4.55 KB |
- Login to post comments
Comments
Brent (not verified) - Thu, 12/31/2009 - 00:32
Be warned when using castor for real production applications. It's very buggy and is poorly written. I believe the main marshall() method of caster is near 1000 lines of code.
matthew (not verified) - Thu, 12/31/2009 - 15:50
I've tried it with jaxb and it works great - it can also be used to generate test artifacts for regression that can be verified with xmlunit. This capability has been around for several years though.
&#8220;Social comments and analytics for this post & (not verified) - Thu, 05/06/2010 - 07:17
[...] http://united-coders.com/phillip-steffensen/spring-module-oxm-a-new-feat... [...]
[Introduzione] Marshalling XML Utilizzando Spring 3 OXM & (not verified) - Fri, 09/17/2010 - 13:36
[...] http://united-coders.com/phillip-steffensen/spring-module-oxm-a-new-feat... (E’ presente un ottimo esempio, su cui costruire la base della nostra applicazione); [...]
Anonymous (not verified) - Tue, 11/23/2010 - 09:10
great post. Anyways, i wanna share also a spoon-feed tutorial on Spring MVC
http://www.adobocode.com/spring/a-spring-web-mvc-tutorial
and add step-by-step Hibernate JPA capabilities tutorial to it:
http://www.adobocode.com/spring/adding-crud-capability-to-spring-mvc
hope it will help people!
Kalai (not verified) - Tue, 11/23/2010 - 13:33
I tried to do marshalling of an object to an xml string and i need to have the <?xml version="1.0" encoding="UTF-8"?> removed from the marshalled xml string. Any ideas of ignoring or removing it during the Marshalling process.
Thanks
Kalai
machello (not verified) - Sat, 06/11/2011 - 21:06
How do i enable schema validation using oxm namespace?