573 lines
19 KiB
Plaintext
573 lines
19 KiB
Plaintext
[[oxm]]
|
|
= Marshalling XML by Using Object-XML Mappers
|
|
|
|
|
|
|
|
[[oxm-introduction]]
|
|
== Introduction
|
|
|
|
This chapter, describes Spring's Object-XML Mapping support. Object-XML
|
|
Mapping (O-X mapping for short) is the act of converting an XML document to and from
|
|
an object. This conversion process is also known as XML Marshalling, or XML
|
|
Serialization. This chapter uses these terms interchangeably.
|
|
|
|
Within the field of O-X mapping, a marshaller is responsible for serializing an
|
|
object (graph) to XML. In similar fashion, an unmarshaller deserializes the XML to
|
|
an object graph. This XML can take the form of a DOM document, an input or output
|
|
stream, or a SAX handler.
|
|
|
|
Some of the benefits of using Spring for your O/X mapping needs are:
|
|
|
|
* xref:data-access/oxm.adoc#oxm-ease-of-configuration[Ease of configuration]
|
|
* xref:data-access/oxm.adoc#oxm-consistent-interfaces[Consistent Interfaces]
|
|
* xref:data-access/oxm.adoc#oxm-consistent-exception-hierarchy[Consistent Exception Hierarchy]
|
|
|
|
|
|
[[oxm-ease-of-configuration]]
|
|
=== Ease of configuration
|
|
|
|
Spring's bean factory makes it easy to configure marshallers, without needing to
|
|
construct JAXB context, JiBX binding factories, and so on. You can configure the marshallers
|
|
as you would any other bean in your application context. Additionally, XML namespace-based
|
|
configuration is available for a number of marshallers, making the configuration even
|
|
simpler.
|
|
|
|
|
|
[[oxm-consistent-interfaces]]
|
|
=== Consistent Interfaces
|
|
|
|
Spring's O-X mapping operates through two global interfaces: {api-spring-framework}/oxm/Marshaller.html[`Marshaller`] and
|
|
{api-spring-framework}/oxm/Unmarshaller.html[`Unmarshaller`]. These abstractions let you switch O-X mapping frameworks
|
|
with relative ease, with little or no change required on the classes that do the
|
|
marshalling. This approach has the additional benefit of making it possible to do XML
|
|
marshalling with a mix-and-match approach (for example, some marshalling performed using JAXB
|
|
and some by XStream) in a non-intrusive fashion, letting you use the strength of each
|
|
technology.
|
|
|
|
|
|
[[oxm-consistent-exception-hierarchy]]
|
|
=== Consistent Exception Hierarchy
|
|
|
|
Spring provides a conversion from exceptions from the underlying O-X mapping tool to its
|
|
own exception hierarchy with the `XmlMappingException` as the root exception.
|
|
These runtime exceptions wrap the original exception so that no information is lost.
|
|
|
|
|
|
|
|
[[oxm-marshaller-unmarshaller]]
|
|
== `Marshaller` and `Unmarshaller`
|
|
|
|
As stated in the xref:data-access/oxm.adoc#oxm-introduction[introduction], a marshaller serializes an object
|
|
to XML, and an unmarshaller deserializes XML stream to an object. This section describes
|
|
the two Spring interfaces used for this purpose.
|
|
|
|
|
|
[[oxm-marshaller]]
|
|
=== Understanding `Marshaller`
|
|
|
|
Spring abstracts all marshalling operations behind the
|
|
`org.springframework.oxm.Marshaller` interface, the main method of which follows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
public interface Marshaller {
|
|
|
|
/**
|
|
* Marshal the object graph with the given root into the provided Result.
|
|
*/
|
|
void marshal(Object graph, Result result) throws XmlMappingException, IOException;
|
|
}
|
|
----
|
|
|
|
The `Marshaller` interface has one main method, which marshals the given object to a
|
|
given `javax.xml.transform.Result`. The result is a tagging interface that basically
|
|
represents an XML output abstraction. Concrete implementations wrap various XML
|
|
representations, as the following table indicates:
|
|
|
|
[[oxm-marshaller-tbl]]
|
|
|===
|
|
| 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`
|
|
|===
|
|
|
|
NOTE: Although the `marshal()` method accepts a plain object as its first parameter, most
|
|
`Marshaller` implementations cannot handle arbitrary objects. Instead, an object class
|
|
must be mapped in a mapping file, be marked with an annotation, be registered with the
|
|
marshaller, or have a common base class. Refer to the later sections in this chapter
|
|
to determine how your O-X technology manages this.
|
|
|
|
|
|
[[oxm-unmarshaller]]
|
|
=== Understanding `Unmarshaller`
|
|
|
|
Similar to the `Marshaller`, we have the `org.springframework.oxm.Unmarshaller`
|
|
interface, which the following listing shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
public interface Unmarshaller {
|
|
|
|
/**
|
|
* Unmarshal the given provided Source into an object graph.
|
|
*/
|
|
Object unmarshal(Source source) throws XmlMappingException, IOException;
|
|
}
|
|
----
|
|
|
|
This interface also has one method, which reads from the given
|
|
`javax.xml.transform.Source` (an XML input abstraction) 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 the following table indicates:
|
|
|
|
[[oxm-unmarshaller-tbl]]
|
|
|===
|
|
| Source 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`
|
|
|===
|
|
|
|
Even though there are two separate marshalling interfaces (`Marshaller` and
|
|
`Unmarshaller`), all implementations in Spring-WS implement both in one class.
|
|
This means that you can wire up one marshaller class and refer to it both as a
|
|
marshaller and as an unmarshaller in your `applicationContext.xml`.
|
|
|
|
|
|
[[oxm-xmlmappingexception]]
|
|
=== Understanding `XmlMappingException`
|
|
|
|
Spring converts exceptions from the underlying O-X mapping tool to its own exception
|
|
hierarchy with the `XmlMappingException` as the root exception.
|
|
These runtime exceptions wrap the original exception so that no information will be lost.
|
|
|
|
Additionally, the `MarshallingFailureException` and `UnmarshallingFailureException`
|
|
provide a distinction between marshalling and unmarshalling operations, even though the
|
|
underlying O-X mapping tool does not do so.
|
|
|
|
The O-X Mapping exception hierarchy is shown in the following figure:
|
|
|
|
image::oxm-exceptions.png[]
|
|
|
|
|
|
|
|
[[oxm-usage]]
|
|
== Using `Marshaller` and `Unmarshaller`
|
|
|
|
You can use Spring's OXM for a wide variety of situations. In the following example, we
|
|
use it to marshal the settings of a Spring-managed application as an XML file. In the following example, we
|
|
use a simple JavaBean to represent the settings:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
----
|
|
public class Settings {
|
|
|
|
private boolean fooEnabled;
|
|
|
|
public boolean isFooEnabled() {
|
|
return fooEnabled;
|
|
}
|
|
|
|
public void setFooEnabled(boolean fooEnabled) {
|
|
this.fooEnabled = fooEnabled;
|
|
}
|
|
}
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
----
|
|
class Settings {
|
|
var isFooEnabled: Boolean = false
|
|
}
|
|
----
|
|
======
|
|
|
|
The application class uses this bean to store its settings. Besides a main method, the
|
|
class has two methods: `saveSettings()` saves the settings bean to a file named
|
|
`settings.xml`, and `loadSettings()` loads these settings again. The following `main()` method
|
|
constructs a Spring application context and calls these two methods:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
----
|
|
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;
|
|
|
|
public class Application {
|
|
|
|
private static final String FILE_NAME = "settings.xml";
|
|
private Settings settings = new Settings();
|
|
private Marshaller marshaller;
|
|
private Unmarshaller unmarshaller;
|
|
|
|
public void setMarshaller(Marshaller marshaller) {
|
|
this.marshaller = marshaller;
|
|
}
|
|
|
|
public void setUnmarshaller(Unmarshaller unmarshaller) {
|
|
this.unmarshaller = unmarshaller;
|
|
}
|
|
|
|
public void saveSettings() throws IOException {
|
|
try (FileOutputStream os = new FileOutputStream(FILE_NAME)) {
|
|
this.marshaller.marshal(settings, new StreamResult(os));
|
|
}
|
|
}
|
|
|
|
public void loadSettings() throws IOException {
|
|
try (FileInputStream is = new FileInputStream(FILE_NAME)) {
|
|
this.settings = (Settings) this.unmarshaller.unmarshal(new StreamSource(is));
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) throws IOException {
|
|
ApplicationContext appContext =
|
|
new ClassPathXmlApplicationContext("applicationContext.xml");
|
|
Application application = (Application) appContext.getBean("application");
|
|
application.saveSettings();
|
|
application.loadSettings();
|
|
}
|
|
}
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
----
|
|
class Application {
|
|
|
|
lateinit var marshaller: Marshaller
|
|
|
|
lateinit var unmarshaller: Unmarshaller
|
|
|
|
fun saveSettings() {
|
|
FileOutputStream(FILE_NAME).use { outputStream -> marshaller.marshal(settings, StreamResult(outputStream)) }
|
|
}
|
|
|
|
fun loadSettings() {
|
|
FileInputStream(FILE_NAME).use { inputStream -> settings = unmarshaller.unmarshal(StreamSource(inputStream)) as Settings }
|
|
}
|
|
}
|
|
|
|
private const val FILE_NAME = "settings.xml"
|
|
|
|
fun main(args: Array<String>) {
|
|
val appContext = ClassPathXmlApplicationContext("applicationContext.xml")
|
|
val application = appContext.getBean("application") as Application
|
|
application.saveSettings()
|
|
application.loadSettings()
|
|
}
|
|
----
|
|
======
|
|
|
|
The `Application` requires both a `marshaller` and an `unmarshaller` property to be set. We
|
|
can do so by using the following `applicationContext.xml`:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<beans>
|
|
<bean id="application" class="Application">
|
|
<property name="marshaller" ref="xstreamMarshaller" />
|
|
<property name="unmarshaller" ref="xstreamMarshaller" />
|
|
</bean>
|
|
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
|
|
</beans>
|
|
----
|
|
|
|
This application context uses XStream, but we could have used any of the other marshaller
|
|
instances described later in this chapter. Note that, by default, XStream does not require
|
|
any further configuration, so the bean definition is rather simple. Also note that the
|
|
`XStreamMarshaller` implements both `Marshaller` and `Unmarshaller`, so we can refer to the
|
|
`xstreamMarshaller` bean in both the `marshaller` and `unmarshaller` property of the
|
|
application.
|
|
|
|
This sample application produces the following `settings.xml` file:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<settings foo-enabled="false"/>
|
|
----
|
|
|
|
|
|
|
|
[[oxm-schema-based-config]]
|
|
== XML Configuration Namespace
|
|
|
|
You can configure marshallers more concisely by using tags from the OXM namespace.
|
|
To make these tags available, you must first reference the appropriate schema in the
|
|
preamble of the XML configuration file. The following example shows how to do so:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<?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" <1>
|
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
|
https://www.springframework.org/schema/beans/spring-beans.xsd
|
|
http://www.springframework.org/schema/oxm
|
|
https://www.springframework.org/schema/oxm/spring-oxm.xsd"> <2>
|
|
----
|
|
<1> Reference the `oxm` schema.
|
|
<2> Specify the `oxm` schema location.
|
|
|
|
|
|
The schema makes the following elements available:
|
|
|
|
* xref:data-access/oxm.adoc#oxm-jaxb2-xsd[`jaxb2-marshaller`]
|
|
* xref:data-access/oxm.adoc#oxm-jibx-xsd[`jibx-marshaller`]
|
|
|
|
Each tag is explained in its respective marshaller's section. As an example, though,
|
|
the configuration of a JAXB2 marshaller might resemble the following:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>
|
|
----
|
|
|
|
|
|
|
|
[[oxm-jaxb]]
|
|
== JAXB
|
|
|
|
The JAXB binding compiler translates a W3C XML Schema into one or more Java classes, a
|
|
`jaxb.properties` file, and possibly some resource files. JAXB also offers a way to
|
|
generate a schema from annotated Java classes.
|
|
|
|
Spring supports the JAXB 2.0 API as XML marshalling strategies, following the
|
|
`Marshaller` and `Unmarshaller` interfaces described in xref:data-access/oxm.adoc#oxm-marshaller-unmarshaller[`Marshaller` and `Unmarshaller`].
|
|
The corresponding integration classes reside in the `org.springframework.oxm.jaxb`
|
|
package.
|
|
|
|
|
|
[[oxm-jaxb2]]
|
|
=== Using `Jaxb2Marshaller`
|
|
|
|
The `Jaxb2Marshaller` class implements both of Spring's `Marshaller` and `Unmarshaller`
|
|
interfaces. It requires a context path to operate. You can set the context path by setting the
|
|
`contextPath` property. The context path is a list of colon-separated Java package
|
|
names that contain schema derived classes. It also offers a `classesToBeBound` property,
|
|
which allows you to set an array of classes to be supported by the marshaller. Schema
|
|
validation is performed by specifying one or more schema resources to the bean, as the following example shows:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<beans>
|
|
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
|
|
<property name="classesToBeBound">
|
|
<list>
|
|
<value>org.springframework.oxm.jaxb.Flight</value>
|
|
<value>org.springframework.oxm.jaxb.Flights</value>
|
|
</list>
|
|
</property>
|
|
<property name="schema" value="classpath:org/springframework/oxm/schema.xsd"/>
|
|
</bean>
|
|
|
|
...
|
|
|
|
</beans>
|
|
----
|
|
|
|
[[oxm-jaxb2-xsd]]
|
|
==== XML Configuration Namespace
|
|
|
|
The `jaxb2-marshaller` element configures a `org.springframework.oxm.jaxb.Jaxb2Marshaller`,
|
|
as the following example shows:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>
|
|
----
|
|
|
|
Alternatively, you can provide the list of classes to bind to the marshaller by using the
|
|
`class-to-be-bound` child element:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<oxm:jaxb2-marshaller id="marshaller">
|
|
<oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Airport"/>
|
|
<oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Flight"/>
|
|
...
|
|
</oxm:jaxb2-marshaller>
|
|
----
|
|
|
|
The following table describes the available attributes:
|
|
|
|
|===
|
|
| Attribute| Description| Required
|
|
|
|
| `id`
|
|
| The ID of the marshaller
|
|
| No
|
|
|
|
| `contextPath`
|
|
| The JAXB Context path
|
|
| No
|
|
|===
|
|
|
|
|
|
|
|
[[oxm-jibx]]
|
|
== JiBX
|
|
|
|
The JiBX framework offers a solution similar to that which Hibernate provides for ORM: A
|
|
binding definition defines the rules for how your Java objects are converted to or from
|
|
XML. After preparing the binding and compiling the classes, a JiBX binding compiler
|
|
enhances the class files and adds code to handle converting instances of the classes
|
|
from or to XML.
|
|
|
|
For more information on JiBX, see the http://jibx.sourceforge.net/[JiBX web
|
|
site]. The Spring integration classes reside in the `org.springframework.oxm.jibx`
|
|
package.
|
|
|
|
|
|
[[oxm-jibx-marshaller]]
|
|
=== Using `JibxMarshaller`
|
|
|
|
The `JibxMarshaller` class implements both the `Marshaller` and `Unmarshaller`
|
|
interface. To operate, it requires the name of the class to marshal in, which you can
|
|
set using the `targetClass` property. Optionally, you can set the binding name by setting the
|
|
`bindingName` property. In the following example, we bind the `Flights` class:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<beans>
|
|
<bean id="jibxFlightsMarshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
|
|
<property name="targetClass">org.springframework.oxm.jibx.Flights</property>
|
|
</bean>
|
|
...
|
|
</beans>
|
|
----
|
|
|
|
A `JibxMarshaller` is configured for a single class. If you want to marshal multiple
|
|
classes, you have to configure multiple `JibxMarshaller` instances with different `targetClass`
|
|
property values.
|
|
|
|
[[oxm-jibx-xsd]]
|
|
==== XML Configuration Namespace
|
|
|
|
The `jibx-marshaller` tag configures a `org.springframework.oxm.jibx.JibxMarshaller`,
|
|
as the following example shows:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/>
|
|
----
|
|
|
|
The following table describes the available attributes:
|
|
|
|
|===
|
|
| Attribute| Description| Required
|
|
|
|
| `id`
|
|
| The ID of the marshaller
|
|
| No
|
|
|
|
| `target-class`
|
|
| The target class for this marshaller
|
|
| Yes
|
|
|
|
| `bindingName`
|
|
| The binding name used by this marshaller
|
|
| No
|
|
|===
|
|
|
|
|
|
|
|
[[oxm-xstream]]
|
|
== XStream
|
|
|
|
XStream is a simple library to serialize objects to XML and back again. It does not
|
|
require any mapping and generates clean XML.
|
|
|
|
For more information on XStream, see the https://x-stream.github.io/[XStream
|
|
web site]. The Spring integration classes reside in the
|
|
`org.springframework.oxm.xstream` package.
|
|
|
|
|
|
[[oxm-xstream-marshaller]]
|
|
=== Using `XStreamMarshaller`
|
|
|
|
The `XStreamMarshaller` does not require any configuration and can be configured in an
|
|
application context directly. To further customize the XML, you can set an alias map,
|
|
which consists of string aliases mapped to classes, as the following example shows:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<beans>
|
|
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
|
|
<property name="aliases">
|
|
<props>
|
|
<prop key="Flight">org.springframework.oxm.xstream.Flight</prop>
|
|
</props>
|
|
</property>
|
|
</bean>
|
|
...
|
|
</beans>
|
|
----
|
|
|
|
[WARNING]
|
|
=====
|
|
By default, XStream lets arbitrary classes be unmarshalled, which can lead to
|
|
unsafe Java serialization effects. As such, we do not recommend using the
|
|
`XStreamMarshaller` to unmarshal XML from external sources (that is, the Web), as this can
|
|
result in security vulnerabilities.
|
|
|
|
If you choose to use the `XStreamMarshaller` to unmarshal XML from an external source,
|
|
set the `supportedClasses` property on the `XStreamMarshaller`, as the following example shows:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
|
|
<property name="supportedClasses" value="org.springframework.oxm.xstream.Flight"/>
|
|
...
|
|
</bean>
|
|
----
|
|
|
|
Doing so ensures that only the registered classes are eligible for unmarshalling.
|
|
|
|
Additionally, you can register
|
|
{api-spring-framework}/oxm/xstream/XStreamMarshaller.html#setConverters(com.thoughtworks.xstream.converters.ConverterMatcher...)[custom
|
|
converters] to make sure that only your supported classes can be unmarshalled. You might
|
|
want to add a `CatchAllConverter` as the last converter in the list, in addition to
|
|
converters that explicitly support the domain classes that should be supported. As a
|
|
result, default XStream converters with lower priorities and possible security
|
|
vulnerabilities do not get invoked.
|
|
=====
|
|
|
|
NOTE: Note that XStream is an XML serialization library, not a data binding library.
|
|
Therefore, it has limited namespace support. As a result, it is rather unsuitable for usage
|
|
within Web Services.
|
|
|
|
|