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

简介

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

在 O-X 映射领域,序列化器负责将对象(图)序列化为 XML。类似地,反序列化器将 XML 反序列化为对象图。此 XML 可以采用 DOM 文档、输入或输出流或 SAX 处理程序的形式。

使用 Spring 进行 O/X 映射的一些优势包括:

易于配置

Spring 的 Bean 工厂使配置序列化器变得容易,无需构建 JAXB 上下文、JiBX 绑定工厂等。您可以像在应用程序上下文中配置其他 Bean 一样配置序列化器。此外,许多序列化器都提供基于 XML 命名空间的配置,使配置更加简单。

一致的接口

Spring 的 O-X 映射通过两个全局接口运行:MarshallerUnmarshaller。这些抽象使您可以轻松地切换 O-X 映射框架,对执行序列化的类几乎不需要进行任何更改。这种方法的额外好处是,它可以以混合匹配的方式进行 XML 序列化(例如,一些序列化使用 JAXB,一些使用 XStream),以非侵入式的方式,让您利用每种技术的优势。

一致的异常层次结构

Spring 提供了从底层 O-X 映射工具的异常到其自身异常层次结构的转换,XmlMappingException 是根异常。这些运行时异常包装了原始异常,因此不会丢失任何信息。

MarshallerUnmarshaller

引言中所述,序列化器将对象序列化为 XML,反序列化器将 XML 流反序列化为对象。本节介绍用于此目的的两个 Spring 接口。

理解 Marshaller

Spring 将所有编组操作抽象到 org.springframework.oxm.Marshaller 接口中,其主要方法如下

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 表示,如下表所示

结果实现 包装 XML 表示

DOMResult

org.w3c.dom.Node

SAXResult

org.xml.sax.ContentHandler

StreamResult

java.io.Filejava.io.OutputStreamjava.io.Writer

虽然 marshal() 方法接受一个普通对象作为其第一个参数,但大多数 Marshaller 实现无法处理任意对象。相反,对象类必须在映射文件中映射,用注解标记,注册到编组器,或具有一个公共基类。请参阅本章后面的部分,以确定您的 O-X 技术如何管理此问题。

理解 Unmarshaller

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

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 表示,如下表所示

源实现 包装 XML 表示

DOMSource

org.w3c.dom.Node

SAXSource

org.xml.sax.InputSourceorg.xml.sax.XMLReader

StreamSource

java.io.Filejava.io.InputStreamjava.io.Reader

尽管有两个单独的编组接口(MarshallerUnmarshaller),但 Spring-WS 中的所有实现都在一个类中实现了这两个接口。这意味着您可以连接一个编组器类,并在您的 applicationContext.xml 中将其同时作为编组器和解组器引用。

理解 XmlMappingException

Spring 将底层 O-X 映射工具的异常转换为其自己的异常层次结构,XmlMappingException 作为根异常。这些运行时异常包装了原始异常,因此不会丢失任何信息。

此外,MarshallingFailureExceptionUnmarshallingFailureException 在编组和反编组操作之间提供了区分,即使底层的 O-X 映射工具没有这样做。

O-X 映射异常层次结构如下图所示

oxm exceptions

使用 MarshallerUnmarshaller

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

  • 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() 将设置 bean 保存到名为 settings.xml 的文件中,而 loadSettings() 再次加载这些设置。以下 main() 方法构造一个 Spring 应用程序上下文并调用这两个方法

  • 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 来做到这一点

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

此示例应用程序会生成以下 settings.xml 文件

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

XML 配置命名空间

您可以通过使用 OXM 命名空间中的标签来更简洁地配置编组器。要使这些标签可用,您必须首先在 XML 配置文件的序言中引用相应的模式。以下示例展示了如何做到这一点

<?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 模式位置。

该模式使以下元素可用

每个标签都在其各自的编组器部分进行了说明。例如,JAXB2 编组器的配置可能类似于以下内容

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

JAXB

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

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

使用 Jaxb2Marshaller

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

<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,如下面的示例所示

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

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

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

下表描述了可用的属性

属性 描述 必需

id

编组器的 ID

contextPath

JAXB 上下文路径

JiBX

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

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

使用 JibxMarshaller

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

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

JibxMarshaller 为单个类配置。如果您想编组多个类,您必须使用不同的 targetClass 属性值配置多个 JibxMarshaller 实例。

XML 配置命名空间

jibx-marshaller 标签配置了一个 org.springframework.oxm.jibx.JibxMarshaller,如下例所示

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

下表描述了可用的属性

属性 描述 必需

id

编组器的 ID

target-class

此编组器的目标类

bindingName

此编组器使用的绑定名称

XStream

XStream 是一个简单的库,用于将对象序列化为 XML 并返回。它不需要任何映射,并生成干净的 XML。

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

使用 XStreamMarshaller

XStreamMarshaller 不需要任何配置,可以直接在应用程序上下文中配置。要进一步自定义 XML,您可以设置别名映射,它包含映射到类的字符串别名,如下例所示

<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 从外部来源(即 Web)反序列化 XML,因为这会导致安全漏洞。

如果您选择使用 XStreamMarshaller 从外部来源反序列化 XML,请在 XStreamMarshaller 上设置 supportedClasses 属性,如下例所示

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

这样做可以确保只有注册的类才有资格进行反序列化。

此外,您可以注册 自定义转换器 以确保只有您支持的类才能被反序列化。除了显式支持应支持的域类的转换器之外,您可能希望将 CatchAllConverter 添加为列表中的最后一个转换器。因此,优先级较低且可能存在安全漏洞的默认 XStream 转换器不会被调用。

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