《Spring官方文档1》17 使用 O/X(Object/XML)映射器对XML进行编组

17 使用 O/X(Object/XML)映射器对XML进行编组

17.1 简介

本章将讨论 Spring 对于 对象/XML 映射的支持。对象/XML 映射,或 O/X 映射,是指将 XML 文档与 XML 文档对象进行互相转换的操作。这一转换操作也被称作 XML 编组,或 XML 序列化。在本章中,这几个概念都指的是同一个东西。 在 O/X 映射中,将一组对象序列化为 XML 的操作是由一个编组器负责的。与之相对,一个反编组器则被用于将 XML 反序列化为一组对象。而这些操作中的 XML 文件来源可能是一份 DOM 文档,一个输入/输出流,或一个 SAX 管理器。 使用 Spring 提供的支持来实现你的 O/X 映射需求具有如下一些好处:

17.1.1 便于配置

Spring 的 bean 工厂使得无需构建 JAXB 上下文、JiBX 绑定工厂就可以配置编组器。你可以像配置你的应用中任何一个 Spring bean 一样地配置编组器。另外,相当一部分编组器可以使用基于 XML 架构(XML Schema-based)的配置来进行设置,这让配置工作变得更为容易。

17.1.2 一致的接口

Spring 的 O/X 映射通过两个全局的接口来执行操作:Marshaller 和 Unmarshaller。这一结构让用户可以在几乎不需要修改编组操作类的前提下,轻易地在不同的 O/X 映射框架之间进行切换。这一结构的另一优势是可以以一种非侵入的方式在代码中混合多种 XML 编组方法(比如有一些编组实现使用 JAXB,而另一些则使用 Castor),从而将各种技术的优势在应用中加以综合利用。

17.1.3 一致的异常继承

Spring 对来自底层 O/X 映射工具的异常进行了转换,以 XmlMappingException 的形式使之成为 Spring 自身异常继承体系的一部分。这些 Spring 运行时异常将初始异常封装其中,因此所有异常信息都会被完整地保留下来。

17.2 编组器与反编组器

就如在“简介”中提到的,一个编组器负责将一个对象序列化成 XML,而一个反编组器则将 XML 流反序列化为一个对象。我们将在本节对 Spring 提供的两个相关接口进行描述。

17.2.1 编组器

Spring 将所有编组操作抽象成了 org.springframework.oxm.Marshaller 中的方法,以下是该接口最主要的一个方法:

[code lang="java"]
public interface Marshaller {
    /**
     * 将对象编组并存放在 Result 中.
     */
    void marshal(Object graph, Result result) throws XmlMappingException, IOException;
}
[/code]

Marshaller 接口有一个主方法用于将一个给定对象编组为一个给定的 javax.xml.transform.Result 实现。这里的 Result 是一个用于代表某种 XML 输出格式的标记接口:不同的 Result 实现会封装不同的 XML 表现形式,详见下表:

Result 实现

封装的 XML 表达方式

DOMResult

org.w3c.dom.Node

SAXResult

org.xml.sax.ContentHandler

StreamResult

java.io.Filejava.io.OutputStream, or java.io.Writer

尽管 marshal() 方法的第一个参数只是一个简单对象,但大多数 Marshaller 实现并不真的能处理任意类型的对象。这个对象要么必须在映射文件中定义过映射关系,要么被注解所修饰,要么在编组器中进行过注册,要么与编组器实现拥有共同的基类。参考后面的章节来确定你所选用的 O/X 技术实现具体是怎么做的。

17.2.2 反编组器

Marshaller 接口相对应,还有一个 org.springframework.oxm.Unmarshaller 接口。

[code lang="java"]
public interface Unmarshaller {
     /**
      * 将来源 XML 反编组成一个对象
      */
     Object unmarshal(Source source) throws XmlMappingException, IOException;
}
[/code]

此接口同样也有一个方法,从 javax.xml.transform.Source (一个抽象的 XML 输入)读取 XML 数据,并返回一个相对应的 Java 对象。和 Result 接口一样,Source 是一个拥有三个具体实现的标记接口。每一个实现封装了一种 XML 表现形式。详见下表:

Source 实现

封装的 XML 表现形式

DOMSource

org.w3c.dom.Node

SAXSource

org.xml.sax.InputSource, and org.xml.sax.XMLReader

StreamSource

java.io.Filejava.io.InputStream, or java.io.Reader

尽管有两个独立的接口(MarshallerUnmarshaller)分别代表编组器与反编组器,Spring 框架中所有对这两个接口的具体实现类都同时实现了这两个接口。这意味着你在 applicationContext.xml 中配置了编组器 bean 之后,可以把它们同时当做编组器与反编组器来使用。

17.2.3 XmlMappingException

Spring 对来自底层 O/X 映射工具的异常进行了转换,以 XmlMappingException 的形式使之成为 Spring 自身异常继承体系的一部分。 这些 Spring 运行时异常将初始异常封装其中,因此所有异常信息都会被完整地保留下来。 额外地,虽然底层的 O/X 映射工具并未提供支持,但 MarshallingFailureExceptionUnmarshallingFailureException 让编组与反编组操作中产生的异常得以能够被区分开来。 以下是 O/X 映射异常的继承层次:

17.3 Marshaller 与 Unmarshaller 的使用

Spring 的 OXM 可被用于十分广泛的场景。在以下的例子中,我们将使用这一功能将一个由 Spring 管理的应用程序的配置编组为一个 XML 文件。我们用了一个简单的 JavaBean 来表示这些配置:

[code lang="java"]
public class Settings {
    private boolean fooEnabled;

    public boolean isFooEnabled() {
        return fooEnabled;
    }

    public void setFooEnabled(boolean fooEnabled) {
        this.fooEnabled = fooEnabled;
    }
}
[/code]

应用程序的主类使用这个 bean 来存放应用的配置信息。除了主要方法外,主类还包含下面两个方法:saveSettings() 将配置 bean 保存成一个名为 settings.xml 的文件, loadSettings() 则将配置信息从 XML 文件中读取出来。另有一个 main() 方法负责构建 Spring 应用上下文,并调用前述两个方法。

[code lang="java"]
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 FILENAME = "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 {
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(FILENAME);
            this.marshaller.marshal(settings, new StreamResult(os));
        } finally {
            if (os != null) {
            os.close();
            }
        }
    }

    public void loadSettings() throws IOException {
        FileInputStream is = null;
        try {
            is = new FileInputStream(FILE_NAME);
            this.settings = (Settings) this.unmarshaller.unmarshal(new StreamSource(is));
        } finally {
            if (is != null) {
            is.close();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        ApplicationContext appContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");
        Application application = (Application) appContext.getBean("application");
        application.saveSettings();
        application.loadSettings();
    }
}
[/code]

需要将 marshallerunmarshaller 这两个属性赋值才能使 Application 正确运行。我们可以使用以下 applicationContext.xml 的内容来实现这一目的:

[code lang="xml"]
<beans>
  <bean id="application" class="Application">
    <property name="marshaller" ref="castorMarshaller" />
    <property name="unmarshaller" ref="castorMarshaller" />
  </bean>
  <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
</beans>
[/code]

该应用中使用了 Castor 这一编组器实例,但我们可以使用在本章稍后描述的任何一个编组器实例来替换 Castor。Castor 默认并不需要任何进一步的配置,所以 bean 定义十分简洁。另外由于 CastorMarshaller 同时实现了 MarshallerUnmarshaller 接口,所以我们可以同时把 castorMarshaller bean 赋值给应用的 marshallerunmarshaller 属性。 此范例应用将会产生如下 settings.xml 文件:

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

17.4 基于 XML 架构的配置

可以使用来自 OXM 命名空间的 XML 标签使对编组器的配置变得更简洁。要使用这些标签,请在 XML 文件开头引用恰当的 XML 架构。以下是一个引用 oxm 的示例:

[code lang="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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd">
[/code]

目前可用的标签有以下这些:

  • jaxb2-marshaller
  • jibx-marshaller
  • castor-marshaller

接下来的章节中将逐一介绍每一个标签。下面先看一个 JAXB2 的配置示例:

[code lang="xml"]
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>
[/code]

17.5 JAXB

JAXB 绑定编译器将 W3C XML 架构实现为一到数个 Java 类,一个 jaxb.properties 文件,可能还会有数个资源文件。JAXB 同时还支持从被注解的 Java 类生成 XML 架构。 Spring 支持基于 17.2 Marshaller 和 Unmarshaller 所提到的 MarshallerUnmarshaller 结构实现的 JAXB 2.0 API 编组策略。相应的类文件都定义在 org.springframework.oxm.jaxb 包下面。

17.5.1 Jaxb2Marshaller

Jaxb2Marshaller 类同时实现了 MarshallerUnmarshaller 接口。这个类需要通过 contextPath 配置好上下文路径才能正常运作。上下文路径是一组由冒号(:)分隔的 Java 包名。这些包下面包含了由 XML 架构所生成的对应 Java 类。另外你可以通过设置一个叫 classesToBeBound 的属性来配置一组可以被编组器支持的类。架构的验证则通过向 bean 中配置一到多个 XML 架构的 xsd 文件资源来实现。下面是一个 bean 的配置示例:

[code lang="xml"]
<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>
[/code]

基于 XML 架构的配置

jaxb2-marshaller 标签 配置了一个 org.springframework.oxm.jaxb.Jaxb2Marshaller 实例。以下是一个配置实例:

[code lang="xml"]
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>
[/code]

如果要配置需要被绑定的类,则可以使用 class-to-be-bound 子标签:

[code lang="xml"]
<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>
[/code]

可用的标签属性如下表:

属性

描述

是否必需

id

编组器的id

no

contextPath

JAXB 上下文路径

no

17.6

Castor XML 映射是一个开源的 XML 绑定框架。它允许使用者将包含在一个 Java 对象模型中的数据转换成 XML 文档,或者反之。Castor 默认并不需要额外的配置就可以使用。但如果使用者希望能够对框架行为拥有更多的控制,则可以通过在配置中引入一个映射文件来达到这一目的。 读者可以从 Castor 的项目主页上了解更多相关内容。Spring 对这一框架的集成代码都在 org.springframework.oxm.castor 包底下。

17.6.1 CastorMarshaller

与 JAXB 类似,CastorMarshaller 类同时实现了 MarshallerUnmarshaller 接口。它可以通过以下配置来被引用:

[code lang="xml"]
<beans>
  <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" />
  ...
</beans>
[/code]

17.6.2 映射

尽管 Castor 的默认编组行为基本能够适应大部分应用场景,用户有时候还是需要一些自定义的能力。这一需求可以通过使用一个 Castor 的映射文件来实现。更多信息请参考 Castor XML Mapping。 映射文件可以通过 mapppingLocation 属性进行设置,如下是一个配置了 classpath 资源的示例:

[code lang="xml"]
<beans>
  <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" >
    <property name="mappingLocation" value="classpath:mapping.xml" />
  </bean>
</beans>
[/code]

基于 XML 架构的配置

一个 castor-marshaller 代表了一个 org.springframework.oxm.castor.CastorMarshaller 实例。示例如下:

[code lang="xml"]
<oxm:castor-marshaller id="marshaller" mapping-location="classpath:org/springframework/oxm/castor/mapping.xml"/>
[/code]

编组器实例可以用两种方式进行配置,一种是(通过 mapping-location 属性)指定映射文件的位置,另一种则是(通过target-classtarget-package 属性)指定包含有相应 XML 描述信息的 Java POJO。后一种方式一般会与通过 XML 架构自动生成 Java 代码的功能结合使用。 以下是可用的标签属性:

属性

描述

是否必需

id

编组器的id

no

encoding

反编组 XML 文件使用的编码

no

target-class

一个 Java POJO 类名,此类中包含一个(由代码自动生成器生成的) XML 类的描述信息

no

target-package

一个包含了(由代码自动生成器生成的) POJO 和相应 Castor XML 描述类的 Java 包名

no

mapping-location

Castor XML 映射文件的位置

no

 

17.7 JiBX

JiBX 框架提供的解决方案思路与 Hibernate 对于 ORM 的解决方案类似:通过一个绑定定义指定了你的 Java 对象与 XML 文件之间互相转换的规则。在准备好绑定并编译了类文件后,一个 JiBX 编译器将会对编译好的类文件进行增强,在其中加入一些辅助代码,并自动添加用于处理在类实例与 XML 文档之间相互转换的操作代码。 请参考 JiBX 官方网站来了解更多信息。Spring 对于框架的集成代码都在 org.springframework.oxm.jibx 包下面。

17.7.1 JibxMarshaller

JiBXMarshaller 类同时实现了 MarshallerUnmarshaller 接口。它需要使用者设置编组的目标类的类名才能正确工作。设置类名的属性是 targetClass。另外还有一个可选属性是 bindingName,用户可以通过这个属性配置绑定名。接下来的示例中,我们将绑定 Flight 类:

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

一个 JibxMarshaller 只能处理一个目标类与 XML 的相互转换,如果你需要编组多个类,你必需配置相应数量的 JibxMarshaller bean,然后在 targetClass 里面指定相应各个类的类名。

基于 XML 架构的配置

jibx-marshaller 标签配置了 org.springframework.oxm.jibx.JibxMarshaller 的实例。以下是一个示例:

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

标签的可用属性如下:

属性

描述

是否必需

id

编组器的id

no

target-class

此编组器所对应的目标类

yes

bindingName

此编组器使用的绑定名

no

17.8 XStream

XStream 是一个用于将对象与 XML 文档进行序列化与反序列化的简单类库。它不需要任何映射关系,并且会生成整齐的 XML 文档。 请参考 XStream 的项目主页以获取更多信息。Spring 对此框架的集成代码都在 org.springframework.oxm.xstream 包下面。

17.8.1 XStreamMarshaller

XStreamMarshaller 类不需进行任何配置便可直接在 applicationContext.xml 文件中直接配置成 bean 进行使用。不过你可以配置一个包含了字符串别名与类之间对应关系的 别名映射表 来实现对 XML 解析结果的自定义:

[code lang="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>
[/code]

XStream 默认允许对任何类进行反编组操作,但这可能会导致安全隐患。因此不建议使用 XStreamMarshaller 对来源于外部(比如公网)的 XML 文档进行反编组。如果你坚持使用 XStreamMarshaller 反编组来自外部的 XML 文档,请如下例演示的一样设置 supportedClasses 属性:

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

设置这一属性能够确保只有指定的类才能够被用于反编组。

更进一步,你可以通过注册 自定义转换器 来确保只有你指定的类才能够被反编组。建议在明确指定了所有转换器后,在列表的最后加上 CatchAllConverter ,这样一来便可确保具有低优先级以及安全风险的 XStream 默认转换器不会被调用。

这里需要注意的是 XStream 是一个 XML 序列化类库,而非一个数据绑定类库,所以它对命名空间的支持有限, 这使得 XStream 并不适合于用在网络服务中。

  • Trackback 关闭
  • 评论 (1)
    • reed7
    • 2017/06/14 2:27上午

    这这这是怎么回事,发布出来的格式怎么变得如此眼花缭乱

return top