XML 項目讀取器與寫入器
Spring Batch 提供交易基礎架構,用於讀取 XML 記錄並將其對應至 Java 物件,以及將 Java 物件寫入為 XML 記錄。
串流 XML 的限制
StAX API 用於 I/O,因為其他標準 XML 解析 API 不符合批次處理需求 (DOM 一次將整個輸入載入記憶體,而 SAX 透過僅允許使用者提供回呼來控制解析過程)。 |
我們需要考量 XML 輸入和輸出在 Spring Batch 中如何運作。首先,有一些概念與檔案讀寫不同,但在 Spring Batch XML 處理中很常見。使用 XML 處理時,不是需要 Token 化的記錄行 (FieldSet
實例),而是假設 XML 資源是與個別記錄對應的「片段」集合,如下圖所示

在上述情境中,「trade」標籤定義為「根元素」。介於 '<trade>' 和 '</trade>' 之間的所有內容都視為一個「片段」。Spring Batch 使用物件/XML 對應 (OXM) 將片段繫結至物件。但是,Spring Batch 不受限於任何特定的 XML 繫結技術。典型的用法是委派給 Spring OXM,它為最熱門的 OXM 技術提供統一的抽象化。Spring OXM 的依賴是選用的,如果需要,您可以選擇實作 Spring Batch 特定的介面。與 OXM 支援的技術之間的關係如下圖所示

在介紹 OXM 以及如何使用 XML 片段來表示記錄之後,我們現在可以更仔細地檢視讀取器和寫入器。
StaxEventItemReader
StaxEventItemReader
設定提供從 XML 輸入串流處理記錄的典型設定。首先,考量以下 StaxEventItemReader
可以處理的 XML 記錄集
<?xml version="1.0" encoding="UTF-8"?>
<records>
<trade xmlns="https://springframework.org/batch/sample/io/oxm/domain">
<isin>XYZ0001</isin>
<quantity>5</quantity>
<price>11.39</price>
<customer>Customer1</customer>
</trade>
<trade xmlns="https://springframework.org/batch/sample/io/oxm/domain">
<isin>XYZ0002</isin>
<quantity>2</quantity>
<price>72.99</price>
<customer>Customer2c</customer>
</trade>
<trade xmlns="https://springframework.org/batch/sample/io/oxm/domain">
<isin>XYZ0003</isin>
<quantity>9</quantity>
<price>99.99</price>
<customer>Customer3</customer>
</trade>
</records>
為了能夠處理 XML 記錄,需要以下項目
-
根元素名稱:構成要對應物件的片段的根元素名稱。範例設定使用值 trade 示範了這一點。
-
資源:代表要讀取檔案的 Spring Resource。
-
Unmarshaller
:Spring OXM 提供的解組工具,用於將 XML 片段對應至物件。
-
Java
-
XML
以下範例示範如何定義 StaxEventItemReader
,其適用於名為 trade
的根元素、data/iosample/input/input.xml
的資源,以及 Java 中的名為 tradeMarshaller
的解組器
@Bean
public StaxEventItemReader itemReader() {
return new StaxEventItemReaderBuilder<Trade>()
.name("itemReader")
.resource(new FileSystemResource("org/springframework/batch/item/xml/domain/trades.xml"))
.addFragmentRootElements("trade")
.unmarshaller(tradeMarshaller())
.build();
}
以下範例示範如何定義 StaxEventItemReader
,其適用於名為 trade
的根元素、data/iosample/input/input.xml
的資源,以及 XML 中的名為 tradeMarshaller
的解組器
<bean id="itemReader" class="org.springframework.batch.item.xml.StaxEventItemReader">
<property name="fragmentRootElementName" value="trade" />
<property name="resource" value="org/springframework/batch/item/xml/domain/trades.xml" />
<property name="unmarshaller" ref="tradeMarshaller" />
</bean>
請注意,在此範例中,我們選擇使用 XStreamMarshaller
,它接受作為地圖傳入的別名,其中第一個鍵和值是片段的名稱 (即根元素) 和要繫結的物件類型。然後,與 FieldSet
類似,對應至物件類型中欄位的其他元素名稱描述為地圖中的鍵/值對。在設定檔中,我們可以使用 Spring 設定公用程式來描述所需的別名。
-
Java
-
XML
以下範例示範如何在 Java 中描述別名
@Bean
public XStreamMarshaller tradeMarshaller() {
Map<String, Class> aliases = new HashMap<>();
aliases.put("trade", Trade.class);
aliases.put("price", BigDecimal.class);
aliases.put("isin", String.class);
aliases.put("customer", String.class);
aliases.put("quantity", Long.class);
XStreamMarshaller marshaller = new XStreamMarshaller();
marshaller.setAliases(aliases);
return marshaller;
}
以下範例示範如何在 XML 中描述別名
<bean id="tradeMarshaller"
class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<util:map id="aliases">
<entry key="trade"
value="org.springframework.batch.samples.domain.trade.Trade" />
<entry key="price" value="java.math.BigDecimal" />
<entry key="isin" value="java.lang.String" />
<entry key="customer" value="java.lang.String" />
<entry key="quantity" value="java.lang.Long" />
</util:map>
</property>
</bean>
在輸入時,讀取器讀取 XML 資源,直到它識別出新的片段即將開始。依預設,讀取器比對元素名稱以識別新的片段即將開始。讀取器從片段建立獨立的 XML 文件,並將文件傳遞至還原序列化器 (通常是 Spring OXM Unmarshaller
的包裝函式),以將 XML 對應至 Java 物件。
總之,此程序類似於以下 Java 程式碼,它使用 Spring 設定提供的注入
StaxEventItemReader<Trade> xmlStaxEventItemReader = new StaxEventItemReader<>();
Resource resource = new ByteArrayResource(xmlResource.getBytes());
Map aliases = new HashMap();
aliases.put("trade","org.springframework.batch.samples.domain.trade.Trade");
aliases.put("price","java.math.BigDecimal");
aliases.put("customer","java.lang.String");
aliases.put("isin","java.lang.String");
aliases.put("quantity","java.lang.Long");
XStreamMarshaller unmarshaller = new XStreamMarshaller();
unmarshaller.setAliases(aliases);
xmlStaxEventItemReader.setUnmarshaller(unmarshaller);
xmlStaxEventItemReader.setResource(resource);
xmlStaxEventItemReader.setFragmentRootElementName("trade");
xmlStaxEventItemReader.open(new ExecutionContext());
boolean hasNext = true;
Trade trade = null;
while (hasNext) {
trade = xmlStaxEventItemReader.read();
if (trade == null) {
hasNext = false;
}
else {
System.out.println(trade);
}
}
StaxEventItemWriter
輸出運作方式與輸入對稱。StaxEventItemWriter
需要 Resource
、marshaller 和 rootTagName
。Java 物件會傳遞至 marshaller (通常是標準 Spring OXM Marshaller),它使用自訂事件寫入器寫入至 Resource
,該寫入器會篩選 OXM 工具為每個片段產生的 StartDocument
和 EndDocument
事件。
-
Java
-
XML
以下 Java 範例使用 MarshallingEventWriterSerializer
@Bean
public StaxEventItemWriter itemWriter(Resource outputResource) {
return new StaxEventItemWriterBuilder<Trade>()
.name("tradesWriter")
.marshaller(tradeMarshaller())
.resource(outputResource)
.rootTagName("trade")
.overwriteOutput(true)
.build();
}
以下 XML 範例使用 MarshallingEventWriterSerializer
<bean id="itemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="resource" ref="outputResource" />
<property name="marshaller" ref="tradeMarshaller" />
<property name="rootTagName" value="trade" />
<property name="overwriteOutput" value="true" />
</bean>
先前的設定設定了三個必要的屬性,並設定了選用的 overwriteOutput=true
屬性,本章稍早提及該屬性用於指定是否可以覆寫現有檔案。
-
Java
-
XML
以下 Java 範例使用與本章稍早顯示的讀取範例中使用的 marshaller 相同的 marshaller
@Bean
public XStreamMarshaller customerCreditMarshaller() {
XStreamMarshaller marshaller = new XStreamMarshaller();
Map<String, Class> aliases = new HashMap<>();
aliases.put("trade", Trade.class);
aliases.put("price", BigDecimal.class);
aliases.put("isin", String.class);
aliases.put("customer", String.class);
aliases.put("quantity", Long.class);
marshaller.setAliases(aliases);
return marshaller;
}
以下 XML 範例使用與本章稍早顯示的讀取範例中使用的 marshaller 相同的 marshaller
<bean id="customerCreditMarshaller"
class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<util:map id="aliases">
<entry key="customer"
value="org.springframework.batch.samples.domain.trade.Trade" />
<entry key="price" value="java.math.BigDecimal" />
<entry key="isin" value="java.lang.String" />
<entry key="customer" value="java.lang.String" />
<entry key="quantity" value="java.lang.Long" />
</util:map>
</property>
</bean>
為了用 Java 範例總結,以下程式碼說明了所有討論的重點,示範了所需屬性的程式化設定
FileSystemResource resource = new FileSystemResource("data/outputFile.xml")
Map aliases = new HashMap();
aliases.put("trade","org.springframework.batch.samples.domain.trade.Trade");
aliases.put("price","java.math.BigDecimal");
aliases.put("customer","java.lang.String");
aliases.put("isin","java.lang.String");
aliases.put("quantity","java.lang.Long");
Marshaller marshaller = new XStreamMarshaller();
marshaller.setAliases(aliases);
StaxEventItemWriter staxItemWriter =
new StaxEventItemWriterBuilder<Trade>()
.name("tradesWriter")
.marshaller(marshaller)
.resource(resource)
.rootTagName("trade")
.overwriteOutput(true)
.build();
staxItemWriter.afterPropertiesSet();
ExecutionContext executionContext = new ExecutionContext();
staxItemWriter.open(executionContext);
Trade trade = new Trade();
trade.setPrice(11.39);
trade.setIsin("XYZ0001");
trade.setQuantity(5L);
trade.setCustomer("Customer1");
staxItemWriter.write(trade);