XML Item Readers and Writers
Spring Batch 提供了事务基础设施,用于读取 XML 记录并将其映射到 Java 对象,以及写入 Java 对象作为 XML 记录。
Spring Batch provides transactional infrastructure for both reading XML records and mapping them to Java objects as well as writing Java objects as XML records.
Constraints on streaming XML
StAX API 用于 I/O,因为其他标准 XML 解析 API 不符合批处理要求(DOM 一次性将整个输入加载到内存中,而 SAX 通过允许用户仅提供回调函数来控制解析进程)。 The StAX API is used for I/O, as other standard XML parsing APIs do not fit batch processing requirements (DOM loads the whole input into memory at once and SAX controls the parsing process by allowing the user to provide only callbacks). |
我们需要考虑在 Spring Batch 中 XML 输入和输出是如何工作的。首先,有一些概念与文件读取和写入不同,但它们在 Spring Batch XML 处理中很常见。在 XML 处理中,假设 XML 资源是分别对应各个记录的“片段”的集合,而不是需要标记化的记录行(FieldSet
实例),如下图所示:
We need to consider how XML input and output works in Spring Batch. First, there are a
few concepts that vary from file reading and writing but are common across Spring Batch
XML processing. With XML processing, instead of lines of records (FieldSet
instances) that need
to be tokenized, it is assumed an XML resource is a collection of 'fragments'
corresponding to individual records, as shown in the following image:
.XML Input
image::xmlinput.png[]
在此场景中,“trade”标记被定义为“根元素”。“<trade>”和 “</trade>”之间的所有内容都被视为一个“片段”。Spring Batch 使用对象/XML 映射 (OXM) 将片段绑定到对象。然而,Spring Batch 并不依赖于任何特定的 XML 绑定技术。典型的使用是委托给 Spring OXM,它为最流行的 OXM 技术提供统一的抽象。依赖于 Spring OXM 是可选的,您可以选择实现 Spring Batch 特定的接口(如果需要的话)。与 OXM 支持的技术之间的关系在以下图片中展示:
The 'trade' tag is defined as the 'root element' in the scenario above. Everything between '<trade>' and '</trade>' is considered one 'fragment'. Spring Batch uses Object/XML Mapping (OXM) to bind fragments to objects. However, Spring Batch is not tied to any particular XML binding technology. Typical use is to delegate to Spring OXM, which provides uniform abstraction for the most popular OXM technologies. The dependency on Spring OXM is optional and you can choose to implement Spring Batch specific interfaces if desired. The relationship to the technologies that OXM supports is shown in the following image: .OXM Binding image::oxm-fragments.png[]
介绍了 OXM 以及如何使用 XML 片段表示记录之后,我们现在可以更仔细地检查读取器和写入器。
With an introduction to OXM and how one can use XML fragments to represent records, we can now more closely examine readers and writers.
StaxEventItemReader
StaxEventItemReader
配置为从 XML 输入流中处理记录提供了一个典型设置。首先,考虑 StaxEventItemReader
可以处理的以下一组 XML 记录:
The StaxEventItemReader
configuration provides a typical setup for the processing of
records from an XML input stream. First, consider the following set of XML records that
the StaxEventItemReader
can process:
<?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 记录,需要满足以下条件:
To be able to process the XML records, the following is needed:
-
Root Element Name: The name of the root element of the fragment that constitutes the object to be mapped. The example configuration demonstrates this with the value of trade.
-
Resource: A Spring Resource that represents the file to read.
-
Unmarshaller
: An unmarshalling facility provided by Spring OXM for mapping the XML fragment to an object.
- Java
-
以下示例展示了如何使用名为
trade
的根元素、资源data/iosample/input/input.xml
和 unmarshaller 在 Java 中定义StaxEventItemReader
:
The following example shows how to define a StaxEventItemReader
that works with a root
element named trade
, a resource of data/iosample/input/input.xml
, and an unmarshaller
called tradeMarshaller
in Java:
@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
-
以下示例展示了如何使用名为
trade
的根元素、资源data/iosample/input/input.xml
和 unmarshaller 在 XML 中定义StaxEventItemReader
:
The following example shows how to define a StaxEventItemReader
that works with a root
element named trade
, a resource of data/iosample/input/input.xml
, and an unmarshaller
called tradeMarshaller
in XML:
<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 配置实用程序来描述必需的别名。
Note that, in this example, we have chosen to use an XStreamMarshaller
, which accepts
an alias passed in as a map with the first key and value being the name of the fragment
(that is, a root element) and the object type to bind. Then, similar to a FieldSet
, the
names of the other elements that map to fields within the object type are described as
key/value pairs in the map. In the configuration file, we can use a Spring configuration
utility to describe the required alias.
- Java
-
以下示例展示了如何在 Java 中描述别名:
The following example shows how to describe the alias in 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
-
以下示例展示了如何在 XML 中描述别名:
The following example shows how to describe the alias in 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 对象。
On input, the reader reads the XML resource until it recognizes that a new fragment is
about to start. By default, the reader matches the element name to recognize that a new
fragment is about to start. The reader creates a standalone XML document from the
fragment and passes the document to a deserializer (typically a wrapper around a Spring
OXM Unmarshaller
) to map the XML to a Java object.
总之,此过程类似于以下 Java 代码,它使用了 Spring 配置提供的注入:
In summary, this procedure is analogous to the following Java code, which uses the injection provided by the Spring configuration:
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
事件。
Output works symmetrically to input. The StaxEventItemWriter
needs a Resource
, a
marshaller, and a rootTagName
. A Java object is passed to a marshaller (typically a
standard Spring OXM Marshaller) which writes to a Resource
by using a custom event
writer that filters the StartDocument
and EndDocument
events produced for each
fragment by the OXM tools.
- Java
-
以下 Java 示例使用了
MarshallingEventWriterSerializer
:
The following Java example uses the MarshallingEventWriterSerializer
:
@Bean
public StaxEventItemWriter itemWriter(Resource outputResource) {
return new StaxEventItemWriterBuilder<Trade>()
.name("tradesWriter")
.marshaller(tradeMarshaller())
.resource(outputResource)
.rootTagName("trade")
.overwriteOutput(true)
.build();
}
- XML
-
以下 XML 示例使用了
MarshallingEventWriterSerializer
:
The following XML example uses the 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
以指定是否可以覆盖现有文件。
The preceding configuration sets up the three required properties and sets the optional
overwriteOutput=true
attrbute, mentioned earlier in this chapter for specifying whether
an existing file can be overwritten.
- Java
-
以下 Java 示例使用与本章前面显示的读取示例中使用的相同编组器:
The following Java example uses the same marshaller as the one used in the reading example shown earlier in the chapter:
@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
-
以下 XML 示例使用与本章前面显示的读取示例中使用的相同编组器:
The following XML example uses the same marshaller as the one used in the reading example shown earlier in the chapter:
<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 示例代码总结说明所有要点,展示必需属性的编程设置:
To summarize with a Java example, the following code illustrates all of the points discussed, demonstrating the programmatic setup of the required properties:
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);