对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

使用对象-XML映射器进行XML编组

< Introduction >

本章介绍 Spring 对对象-XML 映射(Object-XML Mapping)的支持。对象-XML 映射(简称 O-X 映射)是指在 XML 文档与对象之间进行相互转换的操作。该转换过程也被称为 XML 编组(XML Marshalling)或 XML 序列化(XML Serialization)。本章中这些术语可互换使用。spring-doc.cadn.net.cn

在对象-XML映射(O-X mapping)领域中,marshaller 负责将对象(图)序列化为 XML。类似地,unmarshaller 则将 XML 反序列化为对象图。这种 XML 可以采用 DOM 文档、输入或输出流,或者 SAX 处理器的形式。spring-doc.cadn.net.cn

使用 Spring 来满足您的对象/XML(O/X)映射需求的一些优势包括:spring-doc.cadn.net.cn

配置简便

Spring 的 bean 工厂使得配置编组器(marshallers)变得非常简单,无需手动构建 JAXB 上下文、JiBX 绑定工厂等。您可以像配置应用程序上下文中的其他 bean 一样来配置这些编组器。此外,多种编组器还支持基于 XML 命名空间的配置方式,使配置更加简便。spring-doc.cadn.net.cn

一致的接口

Spring 的 O-X 映射通过两个全局接口运作:MarshallerUnmarshaller。这些抽象让您能够相对轻松地切换 O-X 映射框架,而执行编组的类几乎无需更改或完全无需更改。这种方法还有一个额外的好处,即可以以非侵入的方式采用混合搭配的策略进行 XML 编组(例如,部分编组使用 JAXB 完成,另一部分使用 XStream 完成),从而让您能够充分利用每项技术的优势。spring-doc.cadn.net.cn

一致的异常层次结构

Spring 提供了将底层 O-X 映射工具抛出的异常转换为其自身异常体系的功能,其中 XmlMappingException 为根异常。 这些运行时异常包装了原始异常,因此不会丢失任何信息。spring-doc.cadn.net.cn

MarshallerUnmarshaller

正如介绍中所述,编组器(marshaller)将对象序列化为 XML,而解组器(unmarshaller)将 XML 流反序列化为对象。本节介绍了 Spring 中用于此目的的两个接口。spring-doc.cadn.net.cn

理解Marshaller

Spring 将所有编组(marshalling)操作抽象到 org.springframework.oxm.Marshaller 接口之后,其主要方法如下:spring-doc.cadn.net.cn

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 接口有一个主要方法,该方法将给定对象编组(marshal)到指定的 javax.xml.transform.Result 中。2 是一个标记接口,基本上代表了一种 XML 输出的抽象。具体的实现类封装了各种 XML 表示形式,如下表所示:spring-doc.cadn.net.cn

结果实现 包装 XML 表示形式

DOMResultspring-doc.cadn.net.cn

org.w3c.dom.Nodespring-doc.cadn.net.cn

SAXResultspring-doc.cadn.net.cn

org.xml.sax.ContentHandlerspring-doc.cadn.net.cn

StreamResultspring-doc.cadn.net.cn

java.io.File, java.io.OutputStream, or java.io.Writerspring-doc.cadn.net.cn

尽管 marshal() 方法接受一个普通对象作为其第一个参数,但大多数 Marshaller 实现无法处理任意对象。相反,对象类必须在映射文件中进行映射、 通过注解进行标记、向 marshaller 注册,或者具有一个公共基类。请参阅本章后续章节, 以了解您所使用的 O-X 技术如何管理这一点。

理解Unmarshaller

Marshaller 类似,我们还有 org.springframework.oxm.Unmarshaller 接口,如下所示:spring-doc.cadn.net.cn

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表示形式,如下表所示:spring-doc.cadn.net.cn

源码实现 包装 XML 表示形式

DOMSourcespring-doc.cadn.net.cn

org.w3c.dom.Nodespring-doc.cadn.net.cn

SAXSourcespring-doc.cadn.net.cn

org.xml.sax.InputSourceorg.xml.sax.XMLReaderspring-doc.cadn.net.cn

StreamSourcespring-doc.cadn.net.cn

java.io.File, java.io.InputStream, or java.io.Readerspring-doc.cadn.net.cn

尽管存在两个独立的编组接口(MarshallerUnmarshaller),但 Spring-WS 中的所有实现都在同一个类中同时实现了这两个接口。 这意味着你可以在 applicationContext.xml 中配置一个编组器类,并同时将其用作 编组器和解编组器。spring-doc.cadn.net.cn

理解XmlMappingException

Spring 将底层 O-X 映射工具抛出的异常转换为其自身的异常体系,其中 XmlMappingException 为根异常。 这些运行时异常包装了原始异常,因此不会丢失任何信息。spring-doc.cadn.net.cn

此外,MarshallingFailureExceptionUnmarshallingFailureException 对序列化(marshalling)和反序列化(unmarshalling)操作进行了区分,即使底层的 O-X 映射工具本身并未做此区分。spring-doc.cadn.net.cn

O-X 映射异常层次结构如下图所示:spring-doc.cadn.net.cn

oxm exceptions

使用MarshallerUnmarshaller

您可以将 Spring 的 OXM 用于各种场景。在以下示例中, 我们使用它将由 Spring 管理的应用程序的配置设置编组(marshal)为一个 XML 文件。在以下示例中, 我们使用一个简单的 JavaBean 来表示这些设置:spring-doc.cadn.net.cn

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() 将设置 bean 保存到名为 settings.xml 的文件中,而 loadSettings() 则重新加载这些设置。以下的 main() 方法 构建了一个 Spring 应用上下文并调用这两个方法:spring-doc.cadn.net.cn

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 来实现:spring-doc.cadn.net.cn

<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,但我们也可以使用本章后面介绍的其他任意 marshaller 实例。请注意,默认情况下,XStream 无需任何额外配置,因此该 bean 的定义相当简单。另外请注意,XStreamMarshaller 同时实现了 MarshallerUnmarshaller 接口,因此我们可以在应用的 xstreamMarshallermarshaller 属性中都引用 unmarshaller 这个 bean。spring-doc.cadn.net.cn

此示例应用程序会生成以下 settings.xml 文件:spring-doc.cadn.net.cn

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

XML 配置命名空间

您可以通过使用 OXM 命名空间中的标签更简洁地配置编组器(marshallers)。 要使用这些标签,您必须首先在 XML 配置文件的开头部分引用相应的 schema。 以下示例展示了如何进行此操作:spring-doc.cadn.net.cn

<?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 引用 oxm 模式。
2 指定 oxm 的 schema 位置。

该模式提供了以下元素:spring-doc.cadn.net.cn

每个标签都在其对应的编组器(marshaller)部分中进行说明。例如,JAXB2 编组器的配置可能如下所示:spring-doc.cadn.net.cn

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

JAXB(Java 架构绑定)

JAXB绑定编译器将W3C XML Schema转换为一个或多个Java类、一个jaxb.properties文件,以及可能的一些资源文件。JAXB还提供了一种从带注解的Java类生成Schema的方法。spring-doc.cadn.net.cn

Spring 支持 JAXB 2.0 API 作为 XML 编组策略,遵循 MarshallerUnmarshaller 中描述的 MarshallerUnmarshaller 接口。 相应的集成类位于 org.springframework.oxm.jaxb 包中。spring-doc.cadn.net.cn

使用Jaxb2Marshaller

Jaxb2Marshaller 类同时实现了 Spring 的 MarshallerUnmarshaller 接口。它需要一个上下文路径(context path)才能运行。您可以通过设置 contextPath 属性来指定该上下文路径。上下文路径是一个由冒号分隔的 Java 包名列表,这些包中包含由 schema 生成的类。此外,它还提供了一个 classesToBeBound 属性,允许您设置一个由 marshaller 支持的类数组。通过向 bean 指定一个或多个 schema 资源,即可执行 schema 验证,如下例所示:spring-doc.cadn.net.cn

<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 配置命名空间

jaxb2-marshaller 元素用于配置一个 org.springframework.oxm.jaxb.Jaxb2Marshaller,如下例所示:spring-doc.cadn.net.cn

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

或者,你可以通过使用 class-to-be-bound 子元素来提供要绑定到编组器(marshaller)的类列表:spring-doc.cadn.net.cn

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

下表描述了可用的属性:spring-doc.cadn.net.cn

属性 <description> </description> 必填

idspring-doc.cadn.net.cn

编组器的 IDspring-doc.cadn.net.cn

Nospring-doc.cadn.net.cn

contextPathspring-doc.cadn.net.cn

JAXB 上下文路径spring-doc.cadn.net.cn

Nospring-doc.cadn.net.cn

JiBX

JiBX 框架提供了一种类似于 Hibernate 为 ORM 所提供的解决方案:绑定定义规定了 Java 对象与 XML 之间相互转换的规则。在准备好绑定并编译类之后,JiBX 绑定编译器会增强这些类文件,并添加用于处理类实例与 XML 之间相互转换的代码。spring-doc.cadn.net.cn

有关 JiBX 的更多信息,请参见 JiBX 网站。Spring 集成类位于 org.springframework.oxm.jibx 包中。spring-doc.cadn.net.cn

使用JibxMarshaller

JibxMarshaller 类同时实现了 MarshallerUnmarshaller 接口。要正常工作,它需要指定要编组的类名,你可以通过设置 targetClass 属性来完成。此外,你也可以选择通过设置 bindingName 属性来指定绑定名称。在以下示例中,我们将 Flights 类进行绑定:spring-doc.cadn.net.cn

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

一个 JibxMarshaller 是为单个类配置的。如果你想编组多个类,则必须配置多个 JibxMarshaller 实例,并为它们的 targetClass 属性设置不同的值。spring-doc.cadn.net.cn

XML 配置命名空间

jibx-marshaller 标签用于配置一个 org.springframework.oxm.jibx.JibxMarshaller,如下例所示:spring-doc.cadn.net.cn

<oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/>

下表描述了可用的属性:spring-doc.cadn.net.cn

属性 <description> </description> 必填

idspring-doc.cadn.net.cn

编组器的 IDspring-doc.cadn.net.cn

Nospring-doc.cadn.net.cn

target-classspring-doc.cadn.net.cn

此编组器的目标类spring-doc.cadn.net.cn

是的spring-doc.cadn.net.cn

bindingNamespring-doc.cadn.net.cn

此编组器所使用的绑定名称spring-doc.cadn.net.cn

Nospring-doc.cadn.net.cn

XStream

XStream 是一个简单的库,用于将对象序列化为 XML 并可反向还原。它无需任何映射配置,并能生成简洁的 XML。spring-doc.cadn.net.cn

有关 XStream 的更多信息,请参见 XStream 官方网站。Spring 集成类位于 org.springframework.oxm.xstream 包中。spring-doc.cadn.net.cn

使用XStreamMarshaller

XStreamMarshaller 无需任何配置,可直接在应用上下文中进行配置。若要进一步自定义 XML,您可以设置一个别名映射(alias map),该映射由字符串别名与类之间的对应关系组成,如下例所示:spring-doc.cadn.net.cn

<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,因为这可能会引发安全漏洞。spring-doc.cadn.net.cn

如果你选择使用 XStreamMarshaller 从外部源解组 XML, 请在 supportedClasses 上设置 XStreamMarshaller 属性,如下例所示:spring-doc.cadn.net.cn

<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
	<property name="supportedClasses" value="org.springframework.oxm.xstream.Flight"/>
	...
</bean>

这样做可确保只有已注册的类才有资格进行反序列化。spring-doc.cadn.net.cn

此外,您可以注册自定义转换器,以确保只有您支持的类才能被反序列化。除了显式支持应被允许的领域类的转换器外,您可能还希望在转换器列表末尾添加一个CatchAllConverter。这样一来,优先级较低且可能存在安全漏洞的默认 XStream 转换器就不会被调用。spring-doc.cadn.net.cn

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