diff --git a/docs/src/site/docbook/reference/staxReaderWriter.xml b/docs/src/site/docbook/reference/staxReaderWriter.xml new file mode 100644 index 000000000..1b60e8860 --- /dev/null +++ b/docs/src/site/docbook/reference/staxReaderWriter.xml @@ -0,0 +1,281 @@ + + + + Work-in-Progress StaxReaders and Writers + +
+ XML Item Readers and Writers + + 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 + + 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 allowing the user + only to provide callbacks). + + + Lets take a closer look how XML input and output works in 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 (FieldSets) that need to be tokenized, it is + assumed an XML resource is a collection of 'fragments' corresponding to + individual records. Note that OXM tools are designed to work with + standalone XML documents rather than XML fragments cut out of an XML + document, therefore the Spring Batch infrastructure needs to work around + this fact (as described below). + + + + + + + + + + + Figure X: XML Inputs + + + Spring Batch uses Object/XML Mapping (OXM) to bind fragments to + objects. However, Spring Batch is not tied to any particular OXM + technology. Typical use is to delegate OXM + to Spring WS, which provides uniform abstraction for + the most popular OXM technologies. The dependency on Spring WS is optional + and you can choose to implement Spring Batch specific interfaces if + desired. The relationship to the technologies that OXM supports can be + shown as the following: + + + + + + + + + + + Figure X: OXM Binding + + + Now with and introduction into OXM and how one can use XML fragments + to represent records, let's take a closer look at Item Readers and Item + Writers. + +
+ StaxEventItemReader + + The StaxEventItemReader configuration provides a typical setup for + the processing of records from an XML input stream. First, lets examine + a set of xml records that the StaxEventItemReader can process. + + +<?xml version="1.0" encoding="UTF-8"?> +<records> + <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain"> + <isin>XYZ0001</isin> + <quantity>5</quantity> + <price>11.39</price> + <customer>Customer1</customer> + </trade> + <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain"> + <isin>XYZ0002</isin> + <quantity>2</quantity> + <price>72.99</price> + <customer>Customer2c</customer> + </trade> + <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain"> + <isin>XYZ0003</isin> + <quantity>9</quantity> + <price>99.99</price> + <customer>Customer3</customer> + </trade> +</records> + + + To be able to process the XML records we need the following: + + + Root Element Name - this is 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 - This is a Spring Resource that in the case of + this example will abstract the details of opening a file for + reading content. + + + + Fragment Deserializer - this is the UnMarshalling facility + provided by Spring OXM for mapping the XML fragment to an + object. + + + + <property name="itemReader"> + <bean class="org.springframework.batch.io.xml.StaxEventItemReader"> + <property name="fragmentRootElementName" value="trade" /> + <property name="resource" value="data/staxJob/input/20070918.testStream.xmlFileStep.xml" /> + <property name="fragmentDeserializer"> + <bean class="org.springframework.batch.io.xml.oxm.UnmarshallingEventReaderDeserializer"> + <constructor-arg> + <bean class="org.springframework.oxm.xstream.XStreamMarshaller"> + <property name="aliases" ref="aliases" /> + </bean> + </constructor-arg> + </bean> + </property> + </bean> +</property> + + + Notice that in this example we have chosen to use an + XStreamMarshaller that requires an alias passed in as a map with the + first key and value being the name of the fragment (i.e. 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 as + follows: + + + <util:map id="aliases"> + <entry key="trade" + value="org.springframework.batch.sample.domain.Trade" /> + <entry key="isin" value="java.lang.String" /> + <entry key="quantity" value="long" /> + <entry key="price" value="java.math.BigDecimal" /> + <entry key="customer" value="java.lang.String" /> + </util:map> + + + On input the reader reads the XML resource until it recognizes a + new fragment is about to start (by matching the tag name by default). + The reader creates a standalone XML document from the fragment (or at + least makes it appear so) and passes the document to a deserializer + (typically a wrapper around a Spring WS Unmarshaller) to map the XML to + a Java object. + + In summary, if you were to see this in scripted code like Java the + injection provided by the spring configuration would look something like + the following: + + + StaxEventItemReader xmlStaxEventItemReader = new StaxEventItemReader() + Resource resource = new ByteArrayResource(xmlResource.getBytes()) + + Map aliases = new HashMap(); + aliases.put("trade","org.springframework.batch.sample.domain.Trade"); + aliases.put("isin","java.lang.String"); + aliases.put("quantity","long"); + aliases.put("price","java.math.BigDecimal"); + aliases.put("customer","java.lang.String"); + Marshaller marshaller = new XStreamMarshaller(); + marshaller.setAliases(aliases); + xmlStaxEventItemReader.setFragmentDeserializer(new UnmarshallingEventReaderDeserializer(marshaller)); + xmlStaxEventItemReader.setResource(resource); + xmlStaxEventItemReader.setFragmentRootElementName("trade"); + xmlStaxEventItemReader.open(new ExecutionContext()); + + boolean hasNext = true + + while (hasNext) { + trade = xmlStaxEventItemReader.read(); + if (trade == null) { + hasNext = false; + } else { + println trade; + } + } + + +
+ +
+ StaxEventItemWriter + + Output works symetrically to input. The XMLItemWriter needs a + resource, a serializer, and a rootTagName. A java object is passed to a + serializer (typically a wrapper around Spring WS Marshaller) which + writes to output using a custom event writer that filters the + StartDocument and EndDocument events produced for each fragment by the + OXM tools. We'll show this in an example using the + MarshallingEventWriterSerializer. The Spring configuration for this + setup looks as follows: + + <bean class="org.springframework.batch.item.xml.StaxEventItemWriter" id="tradeStaxWriter"> + <property name="resource"value="file:target/test-outputs/20070918.testStream.xmlFileStep.output.xml" /> + <property name="serializer" ref="tradeMarshallingSerializer" /> + <property name="rootTagName" value="trades" /> + <property name="overwriteOutput" value="true" /> +</bean> + + + The configuration sets up the three required properties and + optionally sets the overwriteOutput=true, mentioned earlier in the + chapter for specifying whether an existing file can be overwritten. The + TradeMarshallingSerializer is configured as follows: + + <bean class="org.springframework.batch.item.xml.oxm.MarshallingEventWriterSerializer" id="tradeMarshallingSerializer"> + <constructor-arg> + <bean class="org.springframework.oxm.xstream.XStreamMarshaller"> + <property name="aliases" ref="aliases" /> + </bean> + </constructor-arg> +</bean> + + To summarize with a Java example, the following code illustrates + all of the points discussed. The code demonstrates the programmatic + setup of the required properties. + + StaxEventItemWriter staxItemWriter = new StaxEventItemWriter() +FileSystemResource resource = new FileSystemResource(File.createTempFile("StaxEventWriterOutputSourceTests", "xml")) + +Map aliases = new HashMap(); +aliases.put("trade","org.springframework.batch.sample.domain.Trade"); +aliases.put("isin","java.lang.String"); +aliases.put("quantity","long"); +aliases.put("price","java.math.BigDecimal"); +aliases.put("customer","java.lang.String"); +XStreamMarshaller marshaller = new XStreamMarshaller() +marshaller.setAliases(aliases) + +MarshallingEventWriterSerializer tradeMarshallingSerializer = new MarshallingEventWriterSerializer(marshaller) + +staxItemWriter.setResource(resource) +staxItemWriter.setSerializer(tradeMarshallingSerializer) +staxItemWriter.setRootTagName("trades") +staxItemWriter.setOverwriteOutput(true) + +ExecutionContext executionContext = new ExecutionContext() +staxItemWriter.open(executionContext) +Trade trade = new Trade() +trade.isin = "XYZ0001" +trade.quantity =5 +trade.price = 11.39 +trade.customer = "Customer1" +println trade +staxItemWriter.write(trade) +staxItemWriter.flush() + + For a complete example configuration of XML input and output and a + corresponding Job see the sample xmlStaxJob. +
+
+
\ No newline at end of file