使用物件-XML對映器編組XML

簡介

本章描述 Spring 的物件-XML 對映支援。物件-XML 對映(簡稱 O-X 對映)是將 XML 文件與物件進行相互轉換的操作。此轉換過程也稱為 XML 編組(XML Marshalling)或 XML 序列化(XML Serialization)。本章交替使用這些術語。

在 O-X 對映領域中,marshaller(編組器)負責將物件(圖)序列化為 XML。類似地,unmarshaller(解組器)將 XML 反序列化為物件圖。此 XML 可以採用 DOM 文件、輸入或輸出流或 SAX handler 的形式。

使用 Spring 處理 O/X 對映需求的一些優勢包括:

配置簡單

Spring 的 bean factory 使配置 marshaller 變得簡單,無需構建 JAXB context、JiBX binding factory 等。您可以像配置應用上下文中的任何其他 bean 一樣配置 marshaller。此外,許多 marshaller 支援基於 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。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 實現無法處理任意物件。相反,物件類必須在對映檔案中進行對映,用註解標記,向 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 中配置一個 marshaller 類,並將其同時用作 marshaller 和 unmarshaller。

理解 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 配置檔案的前言中引用相應的 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)
1 引用 oxm schema。
2 指定 oxm schema 位置。

此 schema 提供以下元素:

每個標籤在其各自 marshaller 的部分中進行解釋。例如,JAXB2 marshaller 的配置可能類似於以下內容:

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

JAXB

JAXB binding compiler 將 W3C XML Schema 轉換為一個或多個 Java 類、一個 jaxb.properties 檔案,以及可能的一些資原始檔。JAXB 還提供了一種從帶註解的 Java 類生成 schema 的方法。

Spring 支援 JAXB 2.0 API 作為 XML 編組策略,遵循MarshallerUnmarshaller 中描述的 MarshallerUnmarshaller 介面。相應的整合類位於 org.springframework.oxm.jaxb 包中。

使用 Jaxb2Marshaller

Jaxb2Marshaller 類實現了 Spring 的 MarshallerUnmarshaller 兩個介面。它需要一個 context path 才能操作。您可以透過設定 contextPath 屬性來設定 context path。context path 是一個包含 schema 派生類的以冒號分隔的 Java 包名列表。它還提供了一個 classesToBeBound 屬性,允許您設定 marshaller 支援的類陣列。透過向 bean 指定一個或多個 schema 資源來執行 schema 驗證,如下例所示:

<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 Context path

JiBX

JiBX 框架提供了一個類似於 Hibernate 為 ORM 提供解決方案:一個 binding definition 定義了 Java 物件如何轉換為 XML 或從 XML 轉換的規則。準備好 binding 並編譯類後,JiBX binding compiler 會增強類檔案並新增處理類例項與 XML 相互轉換的程式碼。

有關 JiBX 的更多資訊,請參閱JiBX 網站。Spring 整合類位於 org.springframework.oxm.jibx 包中。

使用 JibxMarshaller

JibxMarshaller 類同時實現了 MarshallerUnmarshaller 介面。為了操作,它需要編組的類的名稱,您可以使用 targetClass 屬性進行設定。您可以選擇透過設定 bindingName 屬性來設定 binding 名稱。在以下示例中,我們綁定了 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 使用的 binding 名稱

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 解組來自外部源(即 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>

這樣做可以確保只有已註冊的類才有資格進行解組。

此外,您可以註冊 custom converters 以確保只有您支援的類可以被解組。您可能希望將 CatchAllConverter 作為列表中的最後一個 converter,此外還包括明確支援應支援的領域類的 converter。這樣一來,具有較低優先順序和可能存在安全漏洞的預設 XStream converter 將不會被呼叫。

請注意,XStream 是一個 XML 序列化庫,而不是資料繫結庫。因此,它的名稱空間支援有限。結果,它不太適合在 Web Services 中使用。