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
StaxEventItemReader
is going to generate.In my case:
where
HeaderType.class
andReceiptType.class
are 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.