XML 項讀取器和寫入器
Spring Batch 提供事務性基礎設施,用於讀取 XML 記錄並將其對映到 Java 物件,以及將 Java 物件寫入為 XML 記錄。
流式 XML 的限制
I/O 使用 StAX API,因為其他標準的 XML 解析 API 不符合批處理要求(DOM 會一次性將整個輸入載入到記憶體中,而 SAX 透過允許使用者僅提供回撥來控制解析過程)。 |
我們需要考慮 Spring Batch 中的 XML 輸入和輸出是如何工作的。首先,有一些概念與檔案讀寫不同,但在 Spring Batch 的 XML 處理中是通用的。在 XML 處理中,不是需要標記化的記錄行(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
以下示例展示瞭如何在 Java 中定義一個 StaxEventItemReader
,它使用名為 trade
的根元素、資源路徑為 data/iosample/input/input.xml
以及名為 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();
}
以下示例展示瞭如何在 XML 中定義一個 StaxEventItemReader
,它使用名為 trade
的根元素、資源路徑為 data/iosample/input/input.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
,它接受一個作為 Map 傳入的別名,Map 的第一個鍵值對是片段的名稱(即根元素)和要繫結的物件型別。然後,類似於 FieldSet
,Map 中以鍵值對形式描述了對映到物件型別中欄位的其他元素的名稱。在配置檔案中,我們可以使用 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),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);