使用对象-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 表示,如下表所示:

Result 实现 包装 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 表示,如下表所示:

Source 实现 包装 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,但我们也可以使用本章后面描述的任何其他 marshaller 实例。请注意,默认情况下,XStream 不需要任何其他配置,因此 bean 定义相当简单。另请注意,XStreamMarshaller 同时实现了MarshallerUnmarshaller,因此我们可以同时在应用程序的marshallerunmarshaller 属性中引用xstreamMarshaller bean。

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

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

XML 配置命名空间

您可以使用 OXM 命名空间中的标签更简洁地配置 marshaller。要使这些标签可用,您必须首先在 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 模式位置。

该模式使以下元素可用:

每个标签在其各自的 marshaller 部分中都有解释。但是,作为一个示例,JAXB2 marshaller 的配置可能类似于以下内容:

<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中描述的接口。相应的集成类位于org.springframework.oxm.jaxb 包中。

使用Jaxb2Marshaller

Jaxb2Marshaller 类同时实现了 Spring 的MarshallerUnmarshaller 接口。它需要一个上下文路径才能运行。您可以通过设置contextPath 属性来设置上下文路径。上下文路径是一个用冒号分隔的 Java 包名称列表,其中包含从模式派生的类。它还提供了一个classesToBeBound 属性,允许您设置一个将由 marshaller 支持的类数组。通过为 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 子元素向 marshaller 提供要绑定的类列表。

<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

marshaller 的 ID

contextPath

JAXB 上下文路径

JiBX

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

marshaller 的 ID

target-class

此 marshaller 的目标类

bindingName

此 marshaller 使用的绑定名称

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 服务中使用。