Spring Module OXM – A new feature of Spring Framework 3.0

Phillip Steffensen's picture

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:

  1. <project>
  2.     <modelVersion>4.0.0</modelVersion>
  3.     <groupId>com.unitedcoders.examples</groupId>
  4.     <artifactId>SpringOxmExample</artifactId>
  5.     <packaging>jar</packaging>
  6.     <version>1.0.0-SNAPSHOT</version>
  7.     <name>SpringOxmExample</name>
  8.     <url>http://united-coders.com</url>
  9.     <build>
  10.         <finalName>SpringOxmExample</finalName>
  11.         <plugins>
  12.             <!-- Compiler Plugin to compile on Java 1.6 -->
  13.             <plugin>
  14.                 <groupId>org.apache.maven.plugins</groupId>
  15.                 <artifactId>maven-compiler-plugin</artifactId>
  16.                 <configuration>
  17.                     <compilerVersion>1.6</compilerVersion>
  18.                     <fork>true</fork>
  19.                     <source>1.6</source>
  20.                     <target>1.6</target>
  21.                 </configuration>
  22.             </plugin>
  23.         </plugins>
  24.     </build>
  25.  
  26.     <!-- Properties -->
  27.     <properties>
  28.         <spring-version>3.0.0.RELEASE</spring-version>
  29.     </properties>
  30.  
  31.     <!-- Dependencies -->
  32.     <dependencies>
  33.         <!-- Spring framework -->
  34.         <dependency>
  35.             <groupId>org.springframework</groupId>
  36.             <artifactId>spring-core</artifactId>
  37.             <version>${spring-version}</version>
  38.         </dependency>
  39.         <dependency>
  40.             <groupId>org.springframework</groupId>
  41.             <artifactId>spring-beans</artifactId>
  42.             <version>${spring-version}</version>
  43.         </dependency>
  44.         <dependency>
  45.             <groupId>org.springframework</groupId>
  46.             <artifactId>spring-oxm</artifactId>
  47.             <version>${spring-version}</version>
  48.         </dependency>
  49.  
  50.         <!-- Castor Xml -->
  51.         <dependency>
  52.             <groupId>org.codehaus.castor</groupId>
  53.             <artifactId>castor</artifactId>
  54.             <version>1.2</version>
  55.         </dependency>
  56.         <dependency>
  57.             <groupId>xerces</groupId>
  58.             <artifactId>xercesImpl</artifactId>
  59.             <version>2.9.1</version>
  60.         </dependency>
  61.  
  62.         <!-- Logging -->
  63.         <dependency>
  64.             <groupId>commons-logging</groupId>
  65.             <artifactId>commons-logging</artifactId>
  66.             <version>1.1.1</version>
  67.         </dependency>
  68.  
  69.         <!-- Test dependencies -->
  70.         <dependency>
  71.             <groupId>junit</groupId>
  72.             <artifactId>junit</artifactId>
  73.             <version>4.4</version>
  74.             <scope>test</scope>
  75.         </dependency>
  76.     </dependencies>
  77. </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:

  1. package com.unitedcoders.examples.spring.oxm.beans;
  2.  
  3. public class Person {
  4.  
  5.     private String firstname;
  6.    
  7.     private String lastname;
  8.    
  9.     private boolean developer;
  10.  
  11.     public String getFirstname() {
  12.         return firstname;
  13.     }
  14.  
  15.     public void setFirstname(String firstname) {
  16.         this.firstname = firstname;
  17.     }
  18.  
  19.     public String getLastname() {
  20.         return lastname;
  21.     }
  22.  
  23.     public void setLastname(String lastname) {
  24.         this.lastname = lastname;
  25.     }
  26.  
  27.     public boolean isDeveloper() {
  28.         return developer;
  29.     }
  30.  
  31.     public void setDeveloper(boolean developer) {
  32.         this.developer = developer;
  33.     }
  34.    
  35. }

Then I created an interface for my O/X-Mapper:

  1. package com.unitedcoders.examples.spring.oxm.mapper;
  2.  
  3. import java.io.IOException;
  4.  
  5. public interface OxMapper {
  6.  
  7.     /**
  8.      * Serializes assigned Object into a file with the assigned name.
  9.      *
  10.      * @param object
  11.      *            - Object that should be serialized
  12.      * @param filename
  13.      *            - name of the XML-file
  14.      * @throws IOException
  15.      */
  16.     public abstract void writeObjectToXml(Object object, String filename) throws IOException;
  17.  
  18.     /**
  19.      * Deserializes an object from the assigned file.
  20.      *
  21.      * @param filename
  22.      *            - name of the file that should be deserialized
  23.      * @return deserialized object
  24.      * @throws IOException
  25.      */
  26.     public abstract Object readObjectFromXml(String filename) throws IOException;
  27.  
  28. }

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:

  1. package com.unitedcoders.examples.spring.oxm.mapper;
  2.  
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6.  
  7. import javax.xml.transform.stream.StreamResult;
  8. import javax.xml.transform.stream.StreamSource;
  9.  
  10. import org.apache.commons.logging.Log;
  11. import org.apache.commons.logging.LogFactory;
  12. import org.springframework.oxm.Marshaller;
  13. import org.springframework.oxm.Unmarshaller;
  14. import org.springframework.oxm.XmlMappingException;
  15.  
  16. public class OxMapperImpl implements OxMapper {
  17.  
  18.     private static final Log LOG = LogFactory.getLog(OxMapperImpl.class);
  19.  
  20.     private Marshaller marshaller;
  21.  
  22.     private Unmarshaller unmarshaller;
  23.  
  24.     public void writeObjectToXml(Object object, String filename) throws IOException {
  25.         FileOutputStream fos = null;
  26.         try {
  27.             fos = new FileOutputStream(filename);
  28.             marshaller.marshal(object, new StreamResult(fos));
  29.         } catch (XmlMappingException e) {
  30.             LOG.error("Xml-Serialization failed due to an XmlMappingException.", e);
  31.         } catch (IOException e) {
  32.             LOG.error("Xml-Serialization failed due to an IOException.", e);
  33.         } finally {
  34.             if (fos != null) {
  35.                 fos.close();
  36.             }
  37.         }
  38.     }
  39.  
  40.     public Object readObjectFromXml(String filename) throws IOException {
  41.         FileInputStream fis = null;
  42.         try {
  43.             fis = new FileInputStream(filename);
  44.             return unmarshaller.unmarshal(new StreamSource(fis));
  45.         } catch (IOException e) {
  46.             LOG.error("Xml-Deserialization failed due to an IOException.", e);
  47.         } finally {
  48.             if (fis != null) {
  49.                 fis.close();
  50.             }
  51.         }
  52.         return null;
  53.     }
  54.  
  55.     public Marshaller getMarshaller() {
  56.         return marshaller;
  57.     }
  58.  
  59.     public void setMarshaller(Marshaller marshaller) {
  60.         this.marshaller = marshaller;
  61.     }
  62.  
  63.     public Unmarshaller getUnmarshaller() {
  64.         return unmarshaller;
  65.     }
  66.  
  67.     public void setUnmarshaller(Unmarshaller unmarshaller) {
  68.         this.unmarshaller = unmarshaller;
  69.     }
  70.    
  71. }

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:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3.  
  4.     <bean id="oxMapper" class="com.unitedcoders.examples.spring.oxm.mapper.OxMapperImpl">
  5.         <property name="marshaller" ref="castorMarshaller" />
  6.         <property name="unmarshaller" ref="castorMarshaller" />
  7.     </bean>
  8.  
  9.     <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" />
  10.  
  11. </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:

  1. package com.unitedcoders.examples.spring.oxm;
  2.  
  3. import java.io.IOException;
  4.  
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;
  6.  
  7. import com.unitedcoders.examples.spring.oxm.beans.Person;
  8. import com.unitedcoders.examples.spring.oxm.mapper.OxMapper;
  9.  
  10. public class ExampleApp {
  11.  
  12.     /**
  13.      * @param args
  14.      */
  15.     public static void main(String[] args) throws IOException {
  16.         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("example-context.xml");
  17.         OxMapper oxMapper = (OxMapper) context.getBean("oxMapper");
  18.  
  19.         // Creating a testperson
  20.         Person person = new Person();
  21.         person.setFirstname("Phil");
  22.         person.setLastname("Steffensen");
  23.         person.setDeveloper(true);
  24.        
  25.         String filename = "person.xml";
  26.         // Serialization
  27.         oxMapper.writeObjectToXml(person, filename);
  28.        
  29.         // Deserialization
  30.         Person deserializedPerson = (Person) oxMapper.readObjectFromXml(filename);
  31.        
  32.         System.out.println("Firstname: " + deserializedPerson.getFirstname());
  33.         System.out.println("Lastname : " + deserializedPerson.getLastname());
  34.         System.out.println("Developer: " + deserializedPerson.isDeveloper());
  35.     }
  36.  
  37. }

After running this code you will see that you have generated a small XML-File containing your person.

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <person developer="true">
  3.     <lastname>Steffensen</lastname>
  4.     <firstname>Phil</firstname>
  5. </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.

  1. package com.unitedcoders.examples.spring.oxm.beans;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. public class Person {
  7.  
  8.     private String firstname = "";
  9.    
  10.     private String lastname = "";
  11.    
  12.     private boolean developer = false;
  13.    
  14.     private List<Person> friends = new ArrayList<Person>();
  15.    
  16.     public String getFirstname() {
  17.         return firstname;
  18.     }
  19.  
  20.     public void setFirstname(String firstname) {
  21.         this.firstname = firstname;
  22.     }
  23.  
  24.     public String getLastname() {
  25.         return lastname;
  26.     }
  27.  
  28.     public void setLastname(String lastname) {
  29.         this.lastname = lastname;
  30.     }
  31.  
  32.     public boolean isDeveloper() {
  33.         return developer;
  34.     }
  35.  
  36.     public void setDeveloper(boolean developer) {
  37.         this.developer = developer;
  38.     }
  39.  
  40.     public List<Person> getFriends() {
  41.         return friends;
  42.     }
  43.  
  44.     public void setFriends(List<Person> friends) {
  45.         this.friends = friends;
  46.     }
  47.    
  48.     public void addFriend(Person friend) {
  49.         friends.add(friend);
  50.     }
  51.    
  52. }

And I changed my Main-Class and added some friends to my person.

  1. package com.unitedcoders.examples.spring.oxm;
  2.  
  3. import java.io.IOException;
  4.  
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;
  6.  
  7. import com.unitedcoders.examples.spring.oxm.beans.Person;
  8. import com.unitedcoders.examples.spring.oxm.mapper.OxMapper;
  9.  
  10. public class ExampleApp {
  11.  
  12.     /**
  13.      * @param args
  14.      */
  15.     public static void main(String[] args) throws IOException {
  16.         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
  17.                 "example-context.xml");
  18.         OxMapper oxMapper = (OxMapper) context.getBean("oxMapper");
  19.  
  20.         Person person = new Person();
  21.         person.setFirstname("Phil");
  22.         person.setLastname("Steffensen");
  23.         person.setDeveloper(true);
  24.        
  25.         Person friend1 = new Person();
  26.         friend1.setFirstname("Christian");
  27.         friend1.setLastname("Harms");
  28.         friend1.setDeveloper(true);
  29.        
  30.         person.addFriend(friend1);
  31.  
  32.         Person friend2 = new Person();
  33.         friend2.setFirstname("John");
  34.         friend2.setLastname("Doe");
  35.         friend2.setDeveloper(false);
  36.        
  37.         person.addFriend(friend2);
  38.  
  39.         String filename = "person_with_friends.xml";
  40.         oxMapper.writeObjectToXml(person, filename);
  41.  
  42.         Person deserializedPerson = (Person) oxMapper.readObjectFromXml(filename);
  43.  
  44.         System.out.println("Firstname: " + deserializedPerson.getFirstname());
  45.         System.out.println("Lastname : " + deserializedPerson.getLastname());
  46.         System.out.println("Developer: " + deserializedPerson.isDeveloper());
  47.         System.out.println("Friends  : " + deserializedPerson.getFriends().size());
  48.     }
  49.  
  50. }

The result looks like this:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <person developer="true">
  3.    <friends xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" developer="true" xsi:type="java:com.unitedcoders.examples.spring.oxm.beans.Person">
  4.      <lastname>Harms</lastname>
  5.      <firstname>Christian</firstname>
  6.   </friends>
  7.   <friends xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" developer="false" xsi:type="java:com.unitedcoders.examples.spring.oxm.beans.Person">
  8.     <lastname>Doe</lastname>
  9.    <firstname>John</firstname>
  10.   </friends>
  11.   <lastname>Steffensen</lastname>
  12.   <firstname>Phil</firstname>
  13. </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:

  1. package com.unitedcoders.examples.spring.oxm.beans;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import javax.xml.bind.annotation.XmlRootElement;
  7.  
  8. @XmlRootElement(name="person")
  9. public class Person {
  10.  
  11.     // ...
  12. }

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.

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3.  
  4.     <context:component-scan base-package="com.unitedcoders.examples.spring.oxm.beans" />
  5.  
  6.     <bean id="oxMapper" class="com.unitedcoders.examples.spring.oxm.mapper.OxMapperImpl">
  7.         <property name="marshaller" ref="jaxbMarshaller" />
  8.         <property name="unmarshaller" ref="jaxbMarshaller" />
  9.     </bean>
  10.  
  11.     <oxm:jaxb2-marshaller id="jaxbMarshaller">
  12.         <oxm:class-to-be-bound name="com.unitedcoders.examples.spring.oxm.beans.Person" />
  13.     </oxm:jaxb2-marshaller>
  14.  
  15. </beans>

The result is now marshalled by JAXB looks like this:

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2. <person>
  3.   <developer>true</developer>
  4.   <firstname>Phil</firstname>
  5.   <friends>
  6.     <developer>true</developer>
  7.     <firstname>Christian</firstname>
  8.     <lastname>Harms</lastname>
  9.   </friends>
  10.   <friends>
  11.     <developer>false</developer>
  12.     <firstname>John</firstname>
  13.     <lastname>Doe</lastname>
  14.   </friends>
  15.   <lastname>Steffensen</lastname>
  16. </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&nbsp;OXM

Spring 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

This 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

Spring OXM (Object/XML Mapping) – A new feature of Spring Framework 3.0 http://is.gd/5IUrg

AttachmentSize
SpringOxmExample.zip_.bz24.55 KB

Comments

Brent's picture

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's picture

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.

[Introduzione] Marshalling XML Utilizzando Spring 3 OXM &amp;amp's picture

[...] 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's picture

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's picture

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's picture

How do i enable schema validation using oxm namespace?