Marshalling XML by Using Object-XML Mappers

Introduction

本章介绍 Spring 的对象 XML 映射支持。对象 XML 映射(简称 O-X 映射)是指 XML 文档对象之间的转换过程。这个转换过程也称为 XML 序列化或 XML 序列转换。本章将这些术语替换使用。

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.

对于 O-X 映射领域,一个序列化器负责将一个对象(图形)序列化为 XML。类似地,一个解串器将 XML 解串为一个对象图形。这个 XML 可以作为 DOM 文档、输入或输出流,或一个 SAX 处理器。

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.

在 O/X 映射中使用 Spring 的一些好处是:

Some of the benefits of using Spring for your O/X mapping needs are:

Ease of configuration

Spring 的 bean 工厂很容易配置序列化器,而无需构建 JAXB 上下文、JiBX 绑定工厂等。你可以像在应用程序上下文中配置任何其他 bean 那样配置序列化器。此外,基于 XML 命名空间的配置可用于多个序列化器,使配置过程变得更简单。

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.

Consistent Interfaces

Spring 的 O-X 映射通过两个全局接口进行操作:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/oxm/Marshaller.html[Marshaller] 和 Unmarshaller。这些抽象使你能够相对容易地切换 O-X 映射框架,几乎不需要对执行编组的类做任何更改。这种方法还有一个好处,就是可以混合匹配来进行 XML 编组(例如,使用 JAXB 执行部分编组,使用 XStream 执行其它部分编组),且不会产生干扰,让你可以使用每种技术的优势。

Spring’s O-X mapping operates through two global interfaces: Marshaller and 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.

Consistent Exception Hierarchy

Spring 提供将基础 O-X 映射工具中异常内容转换为自身的异常层级,其中 XmlMappingException 为根异常。这些运行时异常会包装原始异常,确保不会丢失任何信息。

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.

Marshaller and Unmarshaller

introduction 所述,转换器将对象序列化为 XML,而解组器将 XML 流反序列化为对象。本节介绍用于此目的的两个 Spring 接口。

As stated in the 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.

Understanding Marshaller

Spring 在 org.springframework.oxm.Marshaller 接口之后提取所有编排操作,其主要方法如下:

Spring abstracts all marshalling operations behind the org.springframework.oxm.Marshaller interface, the main method of which follows:

public interface Marshaller {

	/**
	 * Marshal the object graph with the given root into the provided Result.
	 */
	void marshal(Object graph, Result result) throws XmlMappingException, IOException;
}

Marshaller 接口有一个主要方法,它将给定的对象编码为给定的 javax.xml.transform.Result。结果是一个标记接口,基本表示一个 XML 输出抽象。具体实现对不同的 XML 表示进行包装,如下表所示:

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:

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

尽管 marshal() 方法将纯对象作为其第一个参数,但大多数 Marshaller 实现都无法处理任意对象。相反,对象类必须在映射文件中映射、用注释标记、向编组器注册或具有通用基类。请参阅本章后面的章节,以确定您的 O-X 技术如何管理此项工作。

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.

Understanding Unmarshaller

类似于 Marshaller,我们也有 org.springframework.oxm.Unmarshaller 接口,如下表所示:

Similar to the Marshaller, we have the org.springframework.oxm.Unmarshaller interface, which the following listing shows:

public interface Unmarshaller {

	/**
	 * Unmarshal the given provided Source into an object graph.
	 */
	Object unmarshal(Source source) throws XmlMappingException, IOException;
}

该接口也有一个方法,用于从给定的 javax.xml.transform.Source(一个 XML 输入抽象)读取,并返回读取到的对象。与 Result 一样,Source 是一个标记接口,有三个具体实现。每个实现都包装了一个不同的 XML 表示,如下表所示:

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:

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

尽管有两个单独的编排接口(MarshallerUnmarshaller),Spring-WS 中的所有实现都在一个类中实现了这两个接口。这意味着你可以接线到一个序列化器类,并在 applicationContext.xml 中将它同时用作序列化器和解串器。

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.

Understanding XmlMappingException

Spring 将基础 O-X 映射工具中异常内容转换为自身的异常层级,其中 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.

此外,MarshallingFailureExceptionUnmarshallingFailureException 对编排和解串操作进行了区别,尽管基础 O-X 映射工具并没有这样做。

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.

O-X 映射异常层级如下图所示:

The O-X Mapping exception hierarchy is shown in the following figure:

oxm exceptions

Using Marshaller and Unmarshaller

您可以在多种情况下使用 Spring 的 OXM。在以下示例中,我们使用它将 Spring 管理的应用程序的设置编排为一个 XML 文件。在以下示例中,我们使用一个简单的 JavaBean 来表示设置:

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:

  • Java

  • Kotlin

public class Settings {

	private boolean fooEnabled;

	public boolean isFooEnabled() {
		return fooEnabled;
	}

	public void setFooEnabled(boolean fooEnabled) {
		this.fooEnabled = fooEnabled;
	}
}
class Settings {
	var isFooEnabled: Boolean = false
}

应用程序类用此 bean 来存储其设置。除了一个 main 方法之外,这个类有两个方法:saveSettings() 将 settings bean 存储到一个名为 settings.xml 的文件中,loadSettings() 再次加载这些设置。下面的 main() 方法构建一个 Spring 应用程序上下文,并调用这两个方法:

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:

  • Java

  • Kotlin

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();
	}
}
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()
}

Application 要求设置一个 marshallerunmarshaller 属性。我们可以通过使用以下 applicationContext.xml 来做到这一点:

The Application requires both a marshaller and an unmarshaller property to be set. We can do so by using the following applicationContext.xml:

<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>

此应用程序上下文使用 XStream,但是我们也可以使用本章后面介绍的任何其他序列化器实例。请注意,默认情况下,XStream 不需要进一步配置,所以 bean 定义非常简单。另请注意,XStreamMarshaller 实现了 MarshallerUnmarshaller,因此我们可以在 marshallerunmarshaller 的应用程序属性中引用 xstreamMarshaller bean。

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.

此样例应用会生成以下 settings.xml 文件:

This sample application produces the following settings.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<settings foo-enabled="false"/>

XML Configuration Namespace

您可以使用 OXM 命名空间中的标签更简洁地配置编组器。为了让这些标签可用,您必须首先在 XML 配置文件的序言中引用适当的架构。以下示例展示了如何执行此操作:

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:

<?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:

每个标签在各自编组器的部分中进行了解释。然而,作为示例,JAXB2 编组器的配置可能类似于以下内容:

Each tag is explained in its respective marshaller’s section. As an example, though, the configuration of a JAXB2 marshaller might resemble the following:

<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>

JAXB

JAXB 绑定编译器将 W3C XML 架构转换为一个或多个 Java 类、一个 jaxb.properties 文件以及一些资源文件。JAXB 还提供一种从带注释的 Java 类中生成架构的方式。

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 支持 JAXB 2.0 API 作为 XML 编组策略,遵循 MarshallerUnmarshaller 中描述的接口,分别为 xref:data-access/oxm.adoc#oxm-marshaller-unmarshaller[MarshallerUnmarshaller。相应的集成类位于 org.springframework.oxm.jaxb 包中。

Spring supports the JAXB 2.0 API as XML marshalling strategies, following the Marshaller and Unmarshaller interfaces described in Marshaller and Unmarshaller. The corresponding integration classes reside in the org.springframework.oxm.jaxb package.

Using Jaxb2Marshaller

Jaxb2Marshaller 类实现了 Spring 的 MarshallerUnmarshaller 接口。它需要一个上下文路径才能运行。您可以通过设置 contextPath 属性来设置上下文路径。上下文路径由包含架构派生类的、以冒号分隔的 Java 包名列表组成。它还提供了一个 classesToBeBound 属性,供您设置一个由编组器支持的类数组。通过向 Bean 指定一个或多个架构资源执行架构验证,如下例所示:

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:

<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>

XML Configuration Namespace

jaxb2-marshaller 元素会配置一个 org.springframework.oxm.jaxb.Jaxb2Marshaller,如下例所示:

The jaxb2-marshaller element configures a org.springframework.oxm.jaxb.Jaxb2Marshaller, as the following example shows:

<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>

或者,您可以使用 class-to-be-bound 子元素提供要绑定到编组器的类列表:

Alternatively, you can provide the list of classes to bind to the marshaller by using the class-to-be-bound child element:

<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

JiBX

JiBX 框架提供了一个类似于 Hibernate 为 ORM 提供的解决方案:绑定定义定义了如何将您的 Java 对象转换为 XML 或从 XML 转换 Java 对象的规则。准备绑定并编译类之后,一个 JiBX 绑定编译器会增强类文件并将代码添加到从类实例或转换为 XML 的操作处理中。

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.

有关 JiBX 的更多信息,请参阅 JiBX web site。Spring 集成类位于 org.springframework.oxm.jibx 包中。

For more information on JiBX, see the JiBX web site. The Spring integration classes reside in the org.springframework.oxm.jibx package.

Using JibxMarshaller

JibxMarshaller 类实现了 MarshallerUnmarshaller 接口。要运行,它需要要编组成的类的名称,您可以使用 targetClass 属性进行设置。或者,您可以通过设置 bindingName 属性设置绑定名称。在以下示例中,我们绑定了 Flights 类:

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:

<beans>
	<bean id="jibxFlightsMarshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
		<property name="targetClass">org.springframework.oxm.jibx.Flights</property>
	</bean>
	...
</beans>

JibxMarshaller 为单个类进行配置。如果您要编组多个类,您必须配置多个具有不同 targetClass 属性值的 JibxMarshaller 实例。

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.

XML Configuration Namespace

jibx-marshaller 标记会配置一个 org.springframework.oxm.jibx.JibxMarshaller,如下例所示:

The jibx-marshaller tag configures a org.springframework.oxm.jibx.JibxMarshaller, as the following example shows:

<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

XStream

XStream 是一用于将对象序列化成 XML 并再次返回对象的简单库。它不需要任何映射并会生成简洁的 XML。

XStream is a simple library to serialize objects to XML and back again. It does not require any mapping and generates clean XML.

有关 XStream 的更多信息,请参阅 XStream web site。Spring 集成类位于 org.springframework.oxm.xstream 包中。

For more information on XStream, see the XStream web site. The Spring integration classes reside in the org.springframework.oxm.xstream package.

Using XStreamMarshaller

XStreamMarshaller 无需任何配置,可以直接在应用上下文中配置它。为了进一步自定义 XML,您可以设置一个别名映射,它由映射到类的字符串别名组成,如下例所示:

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:

<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>

默认情况下,XStream 允许取消编组任意类,这可能导致不安全的 Java 序列化效果。因此,我们不建议使用 XStreamMarshaller 从外部来源取消编组 XML(即 Web),因为这可能导致安全漏洞。

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.

如果你选择使用 XStreamMarshaller 从外部源处解组 XML,请设置 XStreamMarshaller 上的 supportedClasses 属性,如下例所示:

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:

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

此外,你可以注册 自定义转换器,以确保存证仅能反编组你支持的类。你可能希望将 CatchAllConverter 添加为列表中的最后一个转换器,除了明确支持应该支持的领域类的转换器之外。结果,具有较低优先级的默认 XStream 转换器和可能的安全漏洞不会被调用。

Additionally, you can register 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.

请注意,XStream 是 XML 序列化库,而不是数据绑定库。因此,它的命名空间支持有限。结果,它不太适合在 Web 服务中使用。

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.