20220507 7. Data Access - Marshalling XML by Using Object-XML Mappers
前言
相关依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
</dependency>
介绍
本章描述了 Spring 的 Object-XML 映射支持。Object-XML 映射(简称 OX 映射)是将 XML 文档与对象相互转换的行为。此转换过程也称为 XML 编组或 XML 序列化。
在 OX 映射领域,编组器负责将对象(图)序列化为 XML。以类似的方式,解组器将 XML 反序列化为对象图。XML 可以采用 DOM 文档、输入或输出流或 SAX 处理程序的形式。
使用 Spring 满足您的 O/X 映射需求的一些好处是:
- 易于配置
- 一致的接口
- 一致的异常层次结构
易于配置
Spring 的 bean 工厂可以轻松配置编组器,无需构建 JAXB 上下文、JiBX 绑定工厂等。您可以像配置应用上下文中的任何其他 bean 一样配置编组器。此外,许多编组器都可以使用基于 XML 命名空间的配置,从而使配置更加简单。
一致的接口
Spring 的 OX 映射通过两个全局接口运行:Marshaller
和 Unmarshaller
。这些抽象使您可以相对轻松地切换 OX 映射框架,而对执行编组的类几乎不需要更改。这种方法还有一个额外的好处,它可以以一种非侵入性的方式使用混合匹配方法(例如,一些使用 JAXB 执行的一些编组,一些使用 XStream 执行的编组)进行 XML 编组,让您使用每个技术。
org.springframework.oxm.Marshaller
org.springframework.oxm.Unmarshaller
一致的异常层次结构
Spring 提供了从底层 OX 映射工具的异常到它自己的异常层次结构的转换,以 XmlMappingException
为根异常。这些运行时异常包装了原始异常,因此不会丢失任何信息。
Marshaller
和 Unmarshaller
编组器将对象序列化为 XML,解组器将 XML 流反序列化为对象。
理解 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.File , java.io.OutputStream 或 java.io.Writer |
理解 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.InputSource 和 org.xml.sax.XMLReader |
StreamSource |
java.io.File , java.io.InputStream 或 java.io.Reader |
尽管有两个单独的编组接口( Marshaller
和 Unmarshaller
),但 Spring-WS 中的所有实现都在一个类中实现。这意味着您可以装配一个编组器类,并在您的 applicationContext.xml
中将其称为编组器和解组器。
理解 XmlMappingException
Spring 将来自底层 OX 映射工具的异常转换为它自己的异常层次结构,XmlMappingException
作为根异常。这些运行时异常包装了原始异常,因此不会丢失任何信息。
此外,MarshallingFailureException
和 UnmarshallingFailureException
提供了编组和解组操作之间的区别,即使底层 OX 映射工具不这样做。
OX 映射异常层级如下图所示:
使用 Marshaller
和 Unmarshaller
在以下示例中,我们将 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
需要设置 marshaller
和 unmarshaller
属性。我们可以通过使用以下 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
实现了 Marshaller
和 Unmarshaller
,因此我们可以在 marshaller
和 unmarshaller
属性中引用 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"
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">
该 schema 使以下标签可用:
每个标签都在其各自的编组器部分进行了解释。但是,例如,JAXB2 编组器的配置可能类似于以下内容:
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>
JAXB
JAXB 绑定编译器将 W3C XML Schema 转换为一个或多个 Java 类、一个 jaxb.properties
文件,可能还有一些资源文件。JAXB 还提供了一种从带注解的 Java 类生成模式的方法。
Spring 支持 JAXB 2.0 API 为 XML 编组策略,遵循 Marshaller
和 Unmarshaller
接口。相应的集成类在 org.springframework.oxm.jaxb
包中。
使用 Jaxb2Marshaller
Jaxb2Marshaller
类同时实现了 Spring 的 Marshaller
和 Unmarshaller
接口。它需要一个上下文路径来操作。您可以通过 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
类同时实现 Marshaller
和 Unmarshaller
接口。要进行操作,它需要被编组的类的名称,您可以使用 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 服务中使用。