You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:02:03