I'm using Spring Batch (with Spring Boot) to read an XML file using StaxEventItemReader in a MultiResourceItemReader. Batch reader is intended to target <Receipt> and process/write for each register from the XML-source.
The problem I have is that I need to write the <Header> contents together with every register. I was able to configure Jaxb2Marshaller in order to read receipts one-by-one (getting a callback to process() for each register), but I cannot figure out how can I make reader/unmarshaller to read both the <Header> and a <Receipt> each time.
Maybe I have to create a ReceiptWrapper class to hold both header + receipt? In that case, how to instruct Jaxb2Marshaller to do so? And how to annotate Wrapper class?
I'm a mess with annotations, reader.setFragmentRootElementNames() and marshaller.setClassesToBeBound().
Is there any simple way to achieve that?
The aim is to concatenate the header at the beginning of every register.
Note: I created Header and Receipt classes via Eclipse JAXB code generation from an XSD I generated.
Here is the structure of the XML to read:
<ProcesosEIAC xsi:schemaLocation="http://www.tirea.es/EIAC/ProcesosEIAC ProcesosEIAC.xsd" xmlns="http://www.tirea.es/EIAC/ProcesosEIAC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<!-- [...] -->
</Header>
<Objects>
<Receipt>
<!-- [...] -->
</Receipt>
[...]
<Receipt>
<!-- [...] -->
</Receipt>
</Objects>
Here is an excerpt of what I have:
// Don't know how this should be...
fileReader.setFragmentRootElementNames(new String[]{"ProcesosEIAC", "Header", "Receipt"});
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(ReceiptWrapper.class /*, HeaderType.class, ReceiptType.class*/);
fileReader.setUnmarshaller(marshaller);
Finally I managed to make it work. In essence from what I've understood, to achieve the result, you must set the root elements of the fragments
StaxEventItemReaderis going to generate.In my case:
where
HeaderType.classandReceiptType.classare the JAXB-generated classes.The trick was to define a common interface or base class for JAXB classes (e.g.
MyXmlTargetElement) so that reader type declaration matches the unmarshalled objects:This allowed to read elements sequentially one-by-one (including
<Header>) and no wrapper class was necessary.Then in the
process(MyXmlTargetElement item)method of Batch ItemProcessor, I checked for the actual type of item using instanceof and when a header has been read, set it to a private member field (lastHeader). Then, when a<Receipt>comes, you already have the previously header stored in that member.