跳到主要内容
版本:7.0.3

使用对象-XML映射器进行XML序列化

Hunyuan 7b 中英对照 Marshalling XML by Using Object-XML Mappers

引言

本章介绍了Spring的对象-XML映射(Object-XML Mapping,简称O-X Mapping)功能。对象-XML映射是指将XML文档转换为对象或将对象转换为XML文档的过程。这一转换过程也被称为XML编组(XML Marshalling)或XML序列化(XML Serialization)。在本章中,我们将这些术语互换使用。

在O-X映射领域,marshaller负责将对象(图)序列化为XML。同样地,unmarshaller则将XML反序列化为对象图。这种XML可以是DOM文档的形式,也可以是输入或输出流的形式,或者是SAX处理程序的形式。

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

配置便捷性

Spring的bean工厂使得配置序列化器(marshalers)变得非常简单,无需构建JAXB上下文、JiBX绑定工厂等。你可以像配置应用程序上下文中的其他bean一样来配置这些序列化器。此外,对于许多序列化器来说,还支持基于XML命名空间的配置方式,进一步简化了配置过程。

一致接口

Spring的O-X映射通过两个全局接口进行操作:MarshallerUnmarshaller。这些抽象层使得你可以相对容易地切换O-X映射框架,而几乎不需要对进行序列化的类进行任何修改。这种方法的另一个好处是,它可以以一种非侵入性的方式实现混合使用多种XML序列化技术(例如,部分序列化使用JAXB,部分使用XStream),从而让你能够充分利用每种技术的优势。

一致的异常层次结构

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

MarshallerUnmarshaller

介绍中所述,序列化器(marshaller)将对象序列化为XML格式,反序列化器(unmarshaller)则将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。结果是一个标记接口(tagging interface),它基本上代表了一种XML输出抽象。具体的实现会封装各种XML表示形式,如下表所示:

结果实现包装的XML表示形式
DOMResultorg.w3c.dom.Node
SAXResultorg.xml.sax.ContentHandler
StreamResultjava.io.File, java.io.OutputStream, 或 java.io.Writer
备注

虽然 marshal() 方法接受一个普通对象作为其第一个参数,但大多数 Marshaller 实现无法处理任意对象。相反,对象类必须在映射文件中进行映射,或者用注释标记,或者向 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表示形式
DOMSourceorg.w3c.dom.Node
SAXSourceorg.xml.sax.InputSource, 和 org.xml.sax.XMLReader
StreamSourcejava.io.File, java.io.InputStream, 或 java.io.Reader

尽管有两个独立的序列化接口(MarshallerUnmarshaller),但在Spring-WS中,所有实现都在一个类中同时实现了这两个接口。这意味着你可以在applicationContext.xml中配置一个序列化类,并将其既作为序列化器(marshaller)也作为反序列化器(unmarshaller)来使用。

理解 XmlMappingException

Spring将底层O-X映射工具抛出的异常转换为它自己的异常层次结构,其中XmlMappingException作为根异常。这些运行时异常会封装原始异常,从而确保不会丢失任何信息。

此外,MarshallingFailureExceptionUnmarshallingFailureException在marshalling(序列化)和unmarshalling(反序列化)操作之间进行了区分,尽管底层的O-X映射工具并没有这样做。

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

oxm异常

使用 MarshallerUnmarshaller

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

public class Settings {

private boolean fooEnabled;

public boolean isFooEnabled() {
return fooEnabled;
}

public void setFooEnabled(boolean fooEnabled) {
this.fooEnabled = fooEnabled;
}
}

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

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

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属性中都引用xstreamMarshaler bean。

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

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

XML配置命名空间

你可以通过使用OXM命名空间中的标签来更简洁地配置marshaller。要使这些标签可用,首先必须在XML配置文件的前言中引用相应的模式(schema)。以下示例展示了如何实现这一点:

<?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>
  • 参考 oxm 模式。

  • 指定 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序列化策略,遵循在Marshaller and Unmarshaller中描述的MarshallerUnmarshaller接口。相应的集成类位于org.springframework.oxm.jaxb包中。

使用 Jaxb2Marshaller

Jaxb2Marshaller 类实现了 Spring 的 MarshallerUnmarshaller 接口。它需要一个上下文路径(context path)才能运行。你可以通过设置 contextPath 属性来指定这个上下文路径。上下文路径是一系列用冒号分隔的 Java 包名,这些包名中包含了模式(schema)派生的类。该类还提供了一个 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序列化器的ID
contextPathJAXB上下文路径

JiBX

JiBX框架提供了一种与Hibernate提供的ORM解决方案类似的方案:绑定定义(binding definition)规定了如何将Java对象转换为XML或从XML转换回Java对象的规则。在准备好绑定配置并编译相关类之后,JiBX绑定编译器(binding compiler)会对类文件进行优化,并添加额外的代码来处理这些类的实例从XML到Java或从Java到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 是为单个类配置的。如果你想对多个类进行序列化(marshaling),则必须配置多个具有不同 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,你可以设置一个别名映射(alias map),该映射由字符串别名与对应的类关联起来,如下例所示:

<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,因为这样可能会引发安全漏洞。

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

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

这样做可以确保只有已注册的类才能被反序列化。

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

备注

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