# 5.使用Object-XML映射封装XML

## 5.1 简介

本章介绍了Spring的Object-XML Mapping支持。对象XML映射（简称O-X映射）是将XML文档与对象进行相互转换的动作。此转换过程也称为XML编组或XML序列化。本章可以互换使用这些术语。

在O-X映射领域中，marshaller负责将对象（图形）序列化为XML。以类似的方式，unmarshaller将XML反序列化为对象图。该XML可以采用DOM文档，输入或输出流或SAX处理程序的形式。

使用Spring满足O / X映射需求的一些好处是：

* 易于配置
* 一致的接口
* 一致的异常层次结构

### 5.1.1 易于配置

使用Spring的bean工厂，可以轻松配置编组器，而无需构造JAXB上下文，JiBX绑定工厂等。您可以像在应用程序上下文中配置任何其他bean一样配置编组器。此外，许多编组人员都可以使用基于XML名称空间的配置，从而使配置更加简单。

### 5.1.2 一致的接口

Spring的O-X映射通过两个全局接口运行：Marshaller和Unmarshaller。这些抽象使您可以相对轻松地切换O-X映射框架，而进行编组的类几乎不需要更改。这种方法还有一个好处，就是可以以非介入方式使用混合匹配方法（例如，一些使用JAXB进行的编组和某些由Castor进行的编组）进行XML编组，从而让您充分利用每种方法的优势技术。

### 5.1.3 一致的异常层次结构

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

## 5.2 Marshaller和Unmarshaller

如引言中所述，封送器将对象序列化为XML，解组器将XML流反序列化为对象。 本节描述了用于此目的的两个Spring接口。

### 5.2.1 了解Marshaller

Spring在org.springframework.oxm.Marshaller接口背后抽象了所有编组操作，其主要方法如下：

```java
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, or java.io.Writer |

即使有两个单独的编组接口（Marshaller和Unmarshaller），Spring-WS中的所有实现都在一个类中实现。 这意味着您可以连接一个编组类，并在applicationContext.xml中将其称为编组和解组。

### 5.2.3 理解XmlMappingException

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

此外，即使基础的O-X映射工具没有这样做，MarshallingFailureException和UnmarshallingFailureException也会在编组和解组操作之间进行区分。

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

![](https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/images/oxm-exceptions.png)

## 使用Marshaller和Unmarshaller

您可以在多种情况下使用Spring的OXM。 在下面的示例中，我们使用它来将Spring托管应用程序的设置作为XML文件进行编组。 在下面的示例中，我们使用一个简单的JavaBean来表示设置：

```java
public class Settings {

    private boolean fooEnabled;

    public boolean isFooEnabled() {
        return fooEnabled;
    }

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

应用程序类使用此bean存储其设置。 除了主要方法之外，该类还有两个方法：saveSettings（）将settings Bean保存到名为settings.xml的文件中，loadSettings（）再次加载这些设置。 下面的main（）方法构造一个Spring应用程序上下文并调用这两个方法：

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

该应用程序需要同时设置封送程序和取消封送程序属性。 我们可以使用以下applicationContext.xml来做到这一点：

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

该应用程序上下文使用Castor，但是我们可以使用本章后面介绍的任何其他编组实例。 请注意，默认情况下，Castor不需要任何进一步的配置，因此Bean的定义非常简单。 还要注意，CastorMarshaller同时实现了Marshaller和Unmarshaller，因此我们可以在应用程序的marshaller和unmarshaller属性中引用castorMarshaller bean。

该示例应用程序生成以下settings.xml文件：

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

## 5.4 XML配置命名空间

您可以使用OXM名称空间中的标签来更简洁地配置编组器。 要使这些标签可用，您必须首先在XML配置文件的序言中引用适当的架构。 以下示例显示了如何执行此操作：

```markup
<?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">
```

当前，该模式使以下元素可用：

jaxb2-marshaller

jibx-marshaller

castor-marshaller

每个标签均在其各自的编组部分中进行了说明。 但是，作为示例，JAXB2编组器的配置可能类似于以下内容：

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

## 5.5 JAXB

JAXB绑定编译器将W3C XML Schema转换为一个或多个Java类，一个jaxb.properties文件以及可能的一些资源文件。 JAXB还提供了一种从带注解的Java类生成模式的方法。

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

### 5.5.1 使用Jaxb2Marshaller

Jaxb2Marshaller类实现了Spring的Marshaller和Unmarshaller接口。它需要上下文路径才能运行。您可以通过设置contextPath属性来设置上下文路径。上下文路径是冒号分隔的Java程序包名称的列表，其中包含模式派生的类。它还提供了classesToBeBound属性，该属性使您可以设置编组支持的类的数组。通过向Bean指定一个或多个模式资源来执行模式验证，如以下示例所示：

```markup
<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，如以下示例所示：

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

或者，您可以使用要绑定的子类元素提供绑定到编组器的类的列表：

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

下表描述了可用的属性：

| Attribute   | Description              | Required |
| ----------- | ------------------------ | -------- |
| id          | The ID of the marshaller | No       |
| contextPath | The JAXB Context path    | No       |

## 5.6 Castor

Castor XML映射是一个开源XML绑定框架。 它使您可以将Java对象模型中包含的数据与XML文档进行相互转换。 默认情况下，它不需要任何进一步的配置，尽管您可以使用映射文件来更好地控制Castor的行为。

有关Castor的更多信息，请参见Castor网站。 Spring集成类位于org.springframework.oxm.castor包中。

### 5.6.1 使用CastorMarshaller

与JAXB一样，CastorMarshaller也实现Marshaller和Unmarshaller接口。 可以按以下方式进行连接：

```markup
<beans>
    <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" />
    ...
</beans>
```

## 5.6.2 Mapping

尽管可以依赖Castor的默认编组行为，但可能需要对其进行更多控制。 您可以通过使用Castor映射文件来获得更多控制。 有关更多信息，请参见Castor XML映射。

您可以通过使用mappingLocation资源属性来设置映射，在以下示例中使用类路径资源来指示：

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

**XML配置命名空间**

castor-marshaller标记配置org.springframework.oxm.castor.CastorMarshaller，如以下示例所示：

```markup
<oxm:castor-marshaller id="marshaller" mapping-location="classpath:org/springframework/oxm/castor/mapping.xml"/>
```

您可以通过两种方式配置marshaller实例：通过指定映射文件的位置（通过mapping-location属性）或通过标识Java POJO（通过target-class或target-package属性）来确定存在其对应XML的Java POJO。 描述符类。 后一种方法通常与从XML模式生成XML代码结合使用。

下表描述了可用的属性：

| Attribute        | Description                                                                                                                                                                     | Required |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| id               | The ID of the marshaller                                                                                                                                                        | No       |
| encoding         | The encoding to use for unmarshalling from XML                                                                                                                                  | No       |
| target-class     | A Java class name for a POJO for which an XML class descriptor is available (as generated through code generation)                                                              | No       |
| target-package   | A Java package name that identifies a package that contains POJOs and their corresponding Castor XML descriptor classes (as generated through code generation from XML schemas) | No       |
| mapping-location | Location of a Castor XML mapping file                                                                                                                                           | No       |

## 5.7 JiBX

JiBX框架提供了与Hibernate为ORM提供的解决方案类似的解决方案：绑定定义定义了Java对象如何与XML相互转换的规则。 在准备好绑定并编译了类之后，JiBX绑定编译器将增强类文件并添加代码以处理将类的实例从XML转换为XML的过程。

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

### 5.7.1 使用JibxMarshaller

JibxMarshaller类同时实现Marshaller和Unmarshaller接口。 要进行操作，需要输入要编组的类的名称，您可以使用targetClass属性进行设置。 （可选）您可以通过设置bindingName属性来设置绑定名称。 在下面的示例中，我们绑定了Flights类：

```markup
<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，如以下示例所示：

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

## 5.8 XStream

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

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

### 5.8.1 使用XStreamMarshaller

XStreamMarshaller不需要任何配置，可以直接在应用程序上下文中进行配置。 要进一步自定义XML，可以设置一个别名映射，该映射由映射到类的字符串别名组成，如以下示例所示：

```markup
<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属性，如以下示例所示：

```markup
<bean id =“ xstreamMarshaller” class =“ org.springframework.oxm.xstream.XStreamMarshaller”>
    <property name =“ supportedClasses” value =“ org.springframework.oxm.xstream.Flight” />
    ...
</ bean>
```

> 这样做可以确保只有注册的班级才有资格进行编组。
>
> 此外，您可以注册自定义转换器，以确保只能解组受支持的类。除了明确支持应支持的域类的转换器之外，您可能还想添加CatchAllConverter作为列表中的最后一个转换器。结果，不会调用具有较低优先级和可能的安全漏洞的默认XStream转换器。
