问题描述
我正在使用 Spring Batch(和 Spring Boot)在 MultiResourceItemReader
中使用 StaxEventItemReader
读取 XML 文件.批处理读取器旨在针对 并处理/写入 XML 源中的每个寄存器.
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.
问题是我需要将 内容与每个寄存器一起编写.我能够配置
Jaxb2Marshaller
以便逐个读取收据(为每个寄存器获取对 process()
的回调),但我不知道如何才能我让 reader/unmarshaller 每次都读取 和
.
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.
也许我必须创建一个 ReceiptWrapper
类来保存标题 + 收据?在这种情况下,如何指示 Jaxb2Marshaller
这样做?以及如何注释 Wrapper 类?我的注释很乱,reader.setFragmentRootElementNames()
和 marshaller.setClassesToBeBound()
.
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.
注意:我通过 Eclipse JAXB 代码从我生成的 XSD 创建了 Header 和 Receipt 类.
Note: I created Header and Receipt classes via Eclipse JAXB code generation from an XSD I generated.
这里是要读取的 XML 的结构:
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>
这是我所拥有的摘录:
// 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);
推荐答案
最后我设法让它发挥作用.本质上,根据我的理解,要实现结果,您必须设置 StaxEventItemReader
将要生成的片段的根元素.
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.
就我而言:
fileReader.setFragmentRootElementNames(new String[]{ "Header", "Receipt" }
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(HeaderType.class, ReceiptType.class);
fileReader.setUnmarshaller(marshaller);
其中 HeaderType.class
和 ReceiptType.class
是 JAXB 生成的类.
where HeaderType.class
and ReceiptType.class
are the JAXB-generated classes.
诀窍是为 JAXB 类(例如 MyXmlTargetElement
)定义一个通用接口或基类,以便读取器类型声明与未编组的对象匹配:
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:
StaxEventItemReader<MyXmlTargetElement> fileReader = new StaxEventItemReader<>();
这允许逐个读取元素(包括 )并且不需要包装类.
This allowed to read elements sequentially one-by-one (including <Header>
) and no wrapper class was necessary.
然后在 Batch ItemProcessor 的 process(MyXmlTargetElement item)
方法中,我使用 instanceof 检查了项目的实际类型,并在读取标头时将其设置为私有成员字段(lastHeader).然后,当 出现时,您已经在该成员中存储了先前的标头.
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.
这篇关于针对多个 XML 节点的 Spring Batch JAXB XML 解组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!