XML Item Readers 和 Writers
Spring Batch 為讀取 XML 記錄並將其對映到 Java 物件以及將 Java 物件寫入為 XML 記錄提供了事務性基礎設施。
|
流式 XML 的約束
StAX API 用於 I/O,因為其他標準 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 資源。
-
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/infrastructure/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.infrastructure.item.xml.StaxEventItemReader">
<property name="fragmentRootElementName" value="trade" />
<property name="resource" value="org/springframework/batch/infrastructure/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、一個編組器和一個 rootTagName。Java 物件被傳遞給編組器(通常是標準的 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.infrastructure.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 示例使用與本章前面所示的讀取示例中使用的相同的編組器
@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 示例使用與本章前面所示的讀取示例中使用的相同的編組器
<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);