XML 项目读取器和写入器
Spring Batch 为读取 XML 记录并将其映射到 Java 对象以及将 Java 对象写入 XML 记录提供了事务基础设施。
流式 XML 的约束
StAX API 用于 I/O,因为其他标准 XML 解析 API 不适合批处理需求(DOM 会一次将整个输入加载到内存中,而 SAX 通过允许用户仅提供回调来控制解析过程)。 |
我们需要考虑 XML 输入和输出在 Spring Batch 中的工作方式。首先,有一些概念与文件读取和写入不同,但在 Spring Batch XML 处理中是通用的。在 XML 处理中,假设 XML 资源是对应于单个记录的“片段”的集合,而不是需要标记化的记录行(FieldSet
实例),如下面的图像所示
在上面的场景中,“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
以下示例显示了如何定义一个 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
、一个编组器和一个 rootTagName
。Java 对象传递给编组器(通常是标准的 Spring OXM 编组器),该编组器通过使用自定义事件写入器写入 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 示例使用与本章前面显示的读取示例中使用的相同的编组器
@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);