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

簡介

本章節描述 Spring 的物件-XML 對應支援。物件-XML 對應(簡稱 O-X 對應)是將 XML 文件轉換為物件以及從物件轉換為 XML 文件的動作。此轉換過程也稱為 XML 編組或 XML 序列化。本章節交替使用這些術語。

在 O-X 對應領域中,marshaller 負責將物件(圖形)序列化為 XML。以類似的方式,unmarshaller 將 XML 反序列化為物件圖形。此 XML 可以採用 DOM 文件、輸入或輸出流或 SAX 處理常式的形式。

將 Spring 用於您的 O/X 對應需求的一些優點包括:

易於設定

Spring 的 bean 工廠可以輕鬆設定 marshaller,而無需建構 JAXB context、JiBX binding factory 等。您可以像設定應用程式 Context 中的任何其他 bean 一樣設定 marshaller。此外,XML 命名空間型設定適用於許多 marshaller,使設定更加簡單。

一致的介面

Spring 的 O-X 對應透過兩個全域介面運作:MarshallerUnmarshaller。這些抽象化讓您可以相對輕鬆地切換 O-X 對應框架,而對執行編組的類別幾乎不需要或完全不需要變更。此方法還有一個額外的好處,就是可以以非侵入式的方式執行混合搭配方法 (例如,使用 JAXB 執行某些編組,而使用 XStream 執行某些編組) 的 XML 編組,讓您可以使用每種技術的優勢。

一致的例外階層

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。結果是一個標記介面,基本上代表 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 中的所有實作都在一個類別中實作了這兩個介面。這表示您可以連接一個 marshaller 類別,並在您的 applicationContext.xml 中將其同時稱為 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 應用程式 Context 並呼叫這兩個方法:

  • 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>

此應用程式 Context 使用 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 路徑才能運作。您可以透過設定 contextPath 屬性來設定 Context 路徑。Context 路徑是以冒號分隔的 Java 套件名稱列表,其中包含 schema 衍生的類別。它還提供了一個 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 路徑

JiBX

JiBX 框架提供了一種類似於 Hibernate 為 ORM 提供的解決方案:繫結定義定義了 Java 物件如何轉換為 XML 或從 XML 轉換的規則。在準備繫結並編譯類別之後,JiBX binding compiler 會增強類別檔案,並新增程式碼以處理類別實例從 XML 或轉換為 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 是為單一類別設定的。如果您想要編組多個類別,則必須設定多個具有不同 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 使用的繫結名稱

XStream

XStream 是一個簡單的程式庫,可將物件序列化為 XML 並再次返回。它不需要任何對應,並產生清晰的 XML。

如需 XStream 的詳細資訊,請參閱 XStream 網站。Spring 整合類別位於 org.springframework.oxm.xstream 套件中。

使用 XStreamMarshaller

XStreamMarshaller 不需要任何設定,並且可以直接在應用程式 Context 中設定。為了進一步自訂 XML,您可以設定別名 Map,該 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>

這樣做可確保只有註冊的類別才有資格進行反編組。

此外,您可以註冊自訂轉換器,以確保只有您支援的類別可以反編組。除了明確支援應支援的網域類別的轉換器之外,您可能還想要將 CatchAllConverter 作為列表中的最後一個轉換器新增。因此,不會調用具有較低優先順序和可能安全漏洞的預設 XStream 轉換器。

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