Spring Batch无法将XML contact元素反序列化为字符串问题
问题:Spring Batch读取XML contact元素时仅获取到空白字符
我有如下示例XML,需要在Spring Batch中读取每个<contact>元素并转换为String,以便后续在处理器中使用dom4j进行处理(实际XML结构远比示例复杂,此示例仅用于复现问题):
<query> <contact> <full_name> <first_name>John</first_name> <last_name>Doe</last_name> </full_name> <street>111 Main St</street> <city>Chicago</city> <state>IL</state> <zip>60610</zip> </contact> <contact> <full_name> <first_name>Jane</first_name> <last_name>Smith</last_name> </full_name> <street>222 Main St</street> <city>Miami</city> <state>FL</state> </contact> </query>
BatchConfiguration.java
@Configuration @EnableBatchProcessing @Slf4j public class BatchConfiguration { @Autowired public JobBuilderFactory jobBuilderFactory; @Autowired public StepBuilderFactory stepBuilderFactory; @Autowired public ContactItemProcessor contactItemProcessor; @Bean public StaxEventItemReader<String> reader() { StaxEventItemReader<String> reader = new StaxEventItemReader<>(); try { Map<String, Class<?>> aliases = new HashMap<>(); aliases.put("contact", String.class); XStreamMarshaller unmarshaller = new XStreamMarshaller(); unmarshaller.setAliases(aliases); reader.setName("itemReader"); reader.setResource(new FileSystemResource("contact-list.xml")); reader.setFragmentRootElementName("contact"); reader.setUnmarshaller(unmarshaller); } catch (Exception e) { log.error("ERROR in StaxEventItemReader :: Error while reading: {}", e.getMessage()); } return reader; } @Bean public FlatFileItemWriter<Contact> writer() { FlatFileItemWriter<Contact> writer = new FlatFileItemWriter<>(); writer.setResource(new FileSystemResource("contact-out.dat")); writer.setLineAggregator(new DelimitedLineAggregator<Contact>() { { setDelimiter(","); setFieldExtractor(new BeanWrapperFieldExtractor<Contact>() { { setNames(new String[] { "firstName", "lastName", "street", "city", "state", "zip" }); } }); } }); return writer; } @Bean("InputJob") @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Job importUserJob(Step step) { return jobBuilderFactory.get("importUserJob") .incrementer(new RunIdIncrementer()) .flow(step) .end() .build(); } @Bean public Step step(FlatFileItemWriter<Contact> writer) { return stepBuilderFactory.get("step") .<String, Contact> chunk(10) .reader(reader()) .processor(contactItemProcessor) .writer(writer) .build(); } }
ContactItemProcessor.java
@Component public class ContactItemProcessor implements ItemProcessor<String, Contact> { @Override public Contact process(String item) throws Exception { // TODO Auto-generated method stub return null; } }
但在上述ContactItemProcessor类中,从阅读器传来的item字符串值为\n\t\t,并未获取到实际的contact元素,不清楚原因所在。
解决方案
问题根源在于你使用的XStreamMarshaller并不适合将XML片段直接转为String。XStreamMarshaller的核心作用是把XML映射为Java对象,当你指定aliases.put("contact", String.class)时,它会尝试提取<contact>元素的文本内容,但你的<contact>内部都是子元素,没有直接文本节点,所以只能返回XML中的空白字符(换行、制表符)。
以下两种方案可以解决问题:
方案一:使用Spring Batch内置的StringMarshaller
Spring Batch提供了StringMarshaller,专门用于将XML片段直接转换为字符串。修改reader的配置如下:
@Bean public StaxEventItemReader<String> reader() { StaxEventItemReader<String> reader = new StaxEventItemReader<>(); try { // 替换XStreamMarshaller为StringMarshaller StringMarshaller marshaller = new StringMarshaller(); reader.setName("itemReader"); reader.setResource(new FileSystemResource("contact-list.xml")); reader.setFragmentRootElementName("contact"); reader.setUnmarshaller(marshaller); } catch (Exception e) { log.error("ERROR in StaxEventItemReader :: Error while reading: {}", e.getMessage()); } return reader; }
方案二:自定义Marshaller实现
如果StringMarshaller无法满足特殊需求,也可以自定义Marshaller来直接获取XML片段的字符串:
public class XmlFragmentMarshaller implements Marshaller<String>, Unmarshaller { @Override public String marshal(Object o) throws IOException { // 序列化方法无需实现,仅用到反序列化 throw new UnsupportedOperationException("Not implemented"); } @Override public Object unmarshal(Source source) throws IOException { if (source instanceof StAXSource) { try { XMLStreamReader reader = ((StAXSource) source).getXMLStreamReader(); StringWriter writer = new StringWriter(); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.transform(source, new StreamResult(writer)); return writer.toString(); } catch (TransformerException e) { throw new IOException("Failed to convert XML fragment to string", e); } } throw new IllegalArgumentException("Only StAXSource is supported"); } }
然后在reader中使用这个自定义Marshaller:
XmlFragmentMarshaller marshaller = new XmlFragmentMarshaller(); reader.setUnmarshaller(marshaller);
修改完成后,处理器就能拿到完整的<contact>元素XML字符串,后续可以正常用dom4j解析处理。
内容的提问来源于stack exchange,提问作者Somebody
相关产品推荐
相关产品推荐

