java.lang.IllegalStateException:Marshaller 必须支持编组对象的类

时间:2021-06-18 07:48:01

标签: xml jaxb spring-batch xml-namespaces stax

我在 spring-boot 应用程序中使用 spring batch。 Spring Boot 版本是 2.3.3.RELEASE。 我有一个复杂的 XML,其中包含 header(文件信息)和 body(交易列表)。我使用 XJC toolXSD 获取 java 类。

为了重现这个错误,我在 Github spring-batch-xml-to-xml

上添加了代码

我需要做的:我必须阅读(unmarshal) XML,在正文中做一些business logic on each transaction element,然后最后写({{1} }) 具有相同标题和更新正文的 XML 文件。

当我尝试编组一个子元素对象时,我得到 IllegalStateException: Marshaller must support the class of the marshalled object。

<块引用>

示例 XML 文件

marshal
<块引用>

Spring 批量配置

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<reportFile xmlns="http://deutsche-boerse.com/DBRegHub" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://deutsche-boerse.com/DBRegHub regulatoryHubUpload_MiFIR_001.60.xsd">
    <fileInformation>
        <sender>11003220</sender>
        <timestamp>2020-12-23T09:05:34Z</timestamp>
        <environment>LOCAL</environment>
        <version>1.0</version>
    </fileInformation>
    <record>
        <transaction>
        </transaction>
        <transaction>
        </transaction>
        <transaction>
        </transaction>
    </record>
</reportFile>

<块引用>

从 XJC 工具生成的事务、报告和报告文件 java 类

package com.trax.europeangateway.config;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.support.CompositeItemProcessor;
import org.springframework.batch.item.support.CompositeItemWriter;
import org.springframework.batch.item.xml.StaxEventItemWriter;
import org.springframework.batch.item.xml.builder.StaxEventItemReaderBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.task.TaskExecutor;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.oxm.xstream.XStreamMarshaller;
import org.springframework.util.ClassUtils;
import org.springframework.web.client.RestTemplate;

import com.trax.europeangateway.itemprocessor.omegaxml.PIExtractorItemProcessor;
import com.trax.europeangateway.itemprocessor.omegaxml.PIRemoverItemProcessor;
import com.trax.europeangateway.itemwriter.omegaxml.EdsClientItemWriter;
import com.trax.europeangateway.itemwriter.omegaxml.ExtendedStaxEventItemWriter;
import com.trax.europeangateway.itemwriter.omegaxml.OmegaXmlFileWriter;
import com.trax.europeangateway.itemwriter.omegaxml.OmegaXmlFooterCallBack;
import com.trax.europeangateway.itemwriter.omegaxml.OmegaXmlHeaderCallBack;
import com.trax.europeangateway.listener.JobResultListener;
import com.trax.europeangateway.listener.StepResultListener;
import com.trax.europeangateway.model.dto.FileInformationHeaderDto;
import com.trax.europeangateway.model.dto.ProcessorWriterDto;
import com.trax.europeangateway.model.dto.xsd.omega.TransactionPositionReport;
import com.trax.europeangateway.service.ExtractHeaderOmegaXml;
import com.trax.europeangateway.util.FileUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
    
    @Autowired
    PIExtractorItemProcessor pIExtractorItemProcessor;
    
    @Autowired
    StepResultListener stepResultListener;
    
    @Autowired
    JobBuilderFactory jobBuilderFactory;
     
    @Autowired
    StepBuilderFactory stepBuilderFactory;
    
    @Autowired
    private FileUtils fileUtils;
    
    @Qualifier("europeanGatewayJobExecutor")
    @Autowired
    private TaskExecutor taskExecutor;
    
    @Value( "${eugateway.batch.chunk.size}" )
    private int chunkSize;
    
    @Qualifier("euGatewayJobLauncher")
    @Bean
    public JobLauncher euGatewayJobLauncher(JobRepository jobRepository) throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(jobRepository);
        jobLauncher.setTaskExecutor(taskExecutor);
        jobLauncher.afterPropertiesSet();
        return jobLauncher;
    }
    
    @JobScope
    @Bean (name = "extractHeaderStep")
    public Step extractHeaderStep(StepBuilderFactory steps , @Value("#{jobParameters['file.path']}") String path) {
        return steps.get("extractHeaderStep")
                .tasklet((contribution, chunkContext) -> {
                    FileInformationHeaderDto fileInformation = new ExtractHeaderOmegaXml().readHeader(path);
                    ExecutionContext jobExecutionContext =  chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
                    jobExecutionContext.put("file.information", fileInformation);
                    return RepeatStatus.FINISHED;
                }).build();
    }
    
    @JobScope
    @Bean (name = "extractAndReplacePersonalDataStep")
    public Step jobStep(ItemStreamReader<TransactionPositionReport> reader,
            CompositeItemProcessor<TransactionPositionReport, 
            ProcessorWriterDto> processor,
            CompositeItemWriter<ProcessorWriterDto> writer,
            StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory.get("extractAndReplacePersonalDataStep")
                .<TransactionPositionReport, ProcessorWriterDto>chunk(chunkSize)
                .reader(reader)
                .processor(processor)
                .writer(writer)
                .listener(stepResultListener)
                .build();
    }

    @Qualifier("omegaXmlJob")
    @Bean
    public Job extractPersonalDataJob(Step extractHeaderStep, Step extractAndReplacePersonalDataStep, JobResultListener jobListener,
            JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory.get("extractAndReplacePersonalDataJob")
                .incrementer(new RunIdIncrementer())
                .start(extractHeaderStep)
                .next(extractAndReplacePersonalDataStep)
                .listener(jobListener)
                .build();
    }

    @Bean
    @StepScope
    public ItemStreamReader<TransactionPositionReport> itemReader(@Value("#{jobParameters['file.path']}") String path) {
        Jaxb2Marshaller transactionMarshaller = new Jaxb2Marshaller();
        transactionMarshaller.setMappedClass(TransactionPositionReport.class);
        transactionMarshaller.setPackagesToScan(ClassUtils.getPackageName(TransactionPositionReport.class));
        transactionMarshaller.setSupportJaxbElementClass(true);
        transactionMarshaller.setSupportDtd(true);
        log.debug("Generating StaxEventItemReader");
 
        return new StaxEventItemReaderBuilder<TransactionPositionReport>()
                .name("transactionPositionReport")
                .resource(new FileSystemResource(path))
                .addFragmentRootElements("transaction")
                .unmarshaller(transactionMarshaller)
                .build();
    }
    
    @Bean
    @JobScope
    OmegaXmlHeaderCallBack getOmegaXmlHeaderCallBack(@Value("#{jobExecutionContext['file.information']}") FileInformationHeaderDto fileInformation){
        return new OmegaXmlHeaderCallBack(fileInformation);
    }
    
    @Bean
    OmegaXmlFooterCallBack getOmegaXmlFooterCallBack(){
        return new OmegaXmlFooterCallBack();
    }
    
    @StepScope
    @Bean(name = "staxTransactionWriter")
    public StaxEventItemWriter<TransactionPositionReport> staxTransactionItemWriter(OmegaXmlHeaderCallBack omegaXmlHeaderCallBack, 
            @Value("#{jobParameters['file.path']}") String path, @Value("#{jobParameters['submission.account']}") String submissionAccount) {
        Resource exportFileResource = new FileSystemResource(fileUtils.getOutputFilePath(path, submissionAccount));
 
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setSupportJaxbElementClass(true);
        marshaller.setClassesToBeBound(TransactionPositionReport.class);
        HashMap<String, String> rootElementAttribs = new HashMap<String, String>();
        rootElementAttribs.put("xmlns", "http://deutsche-boerse.com/DBRegHub");
        rootElementAttribs.put("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
        rootElementAttribs.put("xsi:schemaLocation", "http://deutsche-boerse.com/DBRegHub regulatoryHubUpload_MiFIR_001.60.xsd");
        
         ExtendedStaxEventItemWriter<TransactionPositionReport> writer = new ExtendedStaxEventItemWriter<TransactionPositionReport>();
         writer.setName("transactionWriter");
         writer.setVersion("1.0");
         writer.setResource(exportFileResource);
         writer.setMarshaller(marshaller);
         writer.setRootTagName("reportFile");
         writer.setRootElementAttributes(rootElementAttribs);
         writer.setHeaderCallback(omegaXmlHeaderCallBack);
         writer.setFooterCallback(getOmegaXmlFooterCallBack());
         writer.setShouldDeleteIfEmpty(true);
         writer.setIndenting(true);
         return writer;
    }
    
    @StepScope
    @Bean
    public PIExtractorItemProcessor extractItemProcessor() {
        log.debug("Generating PIExtractorItemProcessor");
        return new PIExtractorItemProcessor();
    }
    
    @Bean
    public PIRemoverItemProcessor removeItemProcessor() {
        log.debug("Generating PIRemoverItemProcessor");
        return new PIRemoverItemProcessor();
    }
    
    @Bean
    @StepScope
    CompositeItemProcessor<TransactionPositionReport, ProcessorWriterDto> extractAndRemoveItemProcessor() {
        log.debug("Generating CompositeItemProcessor");
        CompositeItemProcessor<TransactionPositionReport, ProcessorWriterDto> itemProcessor = new CompositeItemProcessor<>();
        itemProcessor.setDelegates((List<? extends ItemProcessor<?, ?>>) Arrays.asList(extractItemProcessor(), removeItemProcessor()));
        return itemProcessor;
    }
    
    @Bean
    public EdsClientItemWriter<ProcessorWriterDto> edsClientItemWriter() {
        log.debug("Generating EdsClientItemWriter");
        return new EdsClientItemWriter<>();
    }
    
    @Bean
    @StepScope
    public OmegaXmlFileWriter<ProcessorWriterDto> omegaXmlFileWriter(OmegaXmlHeaderCallBack omegaXmlHeaderCallBack, @Value("#{jobParameters['file.path']}") String path, @Value("#{jobParameters['submission.account']}") String submissionAccount) {
        log.debug("Generating OmegaXmlFileWriter");
        return new OmegaXmlFileWriter(staxTransactionItemWriter(omegaXmlHeaderCallBack, path, submissionAccount));
    }
    
    
    @Bean
    @StepScope
    public CompositeItemWriter<ProcessorWriterDto> compositeItemWriter(OmegaXmlHeaderCallBack omegaXmlHeaderCallBack, @Value("#{jobParameters['file.path']}") String path, @Value("#{jobParameters['submission.account']}") String submissionAccount) {
        log.debug("Generating CompositeItemWriter");
        CompositeItemWriter<ProcessorWriterDto> compositeItemWriter = new CompositeItemWriter<>();
        compositeItemWriter.setDelegates(Arrays.asList(edsClientItemWriter(), omegaXmlFileWriter(omegaXmlHeaderCallBack, path, submissionAccount)));
        return compositeItemWriter;
    }
    
    @Bean
    public RestTemplate restTemplate() {
        log.debug("Generating RestTemplate");
        return new RestTemplate();
    }
}

<块引用>

尝试编组时得到的异常堆栈跟踪

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2021.05.18 at 02:21:55 PM BST 
//

   
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for transactionPositionReport complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="transactionPositionReport">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="processingDetails" type="{http://deutsche-boerse.com/DBRegHub}processingDetails"/>
 *         &lt;element name="configurableFields" type="{http://deutsche-boerse.com/DBRegHub}configurableFields" minOccurs="0"/>
 *         &lt;element name="mifir" type="{http://deutsche-boerse.com/DBRegHub}mifirDetails" minOccurs="0"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "transactionPositionReport", propOrder = {
    "processingDetails",
    "configurableFields",
    "mifir"
})
public class TransactionPositionReport {

    @XmlElement(required = true)
    protected ProcessingDetails processingDetails;
    protected ConfigurableFields configurableFields;
    protected MifirDetails mifir;

    /**
     * Gets the value of the processingDetails property.
     * 
     * @return
     *     possible object is
     *     {@link ProcessingDetails }
     *     
     */
    public ProcessingDetails getProcessingDetails() {
        return processingDetails;
    }

    /**
     * Sets the value of the processingDetails property.
     * 
     * @param value
     *     allowed object is
     *     {@link ProcessingDetails }
     *     
     */
    public void setProcessingDetails(ProcessingDetails value) {
        this.processingDetails = value;
    }

    /**
     * Gets the value of the configurableFields property.
     * 
     * @return
     *     possible object is
     *     {@link ConfigurableFields }
     *     
     */
    public ConfigurableFields getConfigurableFields() {
        return configurableFields;
    }

    /**
     * Sets the value of the configurableFields property.
     * 
     * @param value
     *     allowed object is
     *     {@link ConfigurableFields }
     *     
     */
    public void setConfigurableFields(ConfigurableFields value) {
        this.configurableFields = value;
    }

    /**
     * Gets the value of the mifir property.
     * 
     * @return
     *     possible object is
     *     {@link MifirDetails }
     *     
     */
    public MifirDetails getMifir() {
        return mifir;
    }

    /**
     * Sets the value of the mifir property.
     * 
     * @param value
     *     allowed object is
     *     {@link MifirDetails }
     *     
     */
    public void setMifir(MifirDetails value) {
        this.mifir = value;
    }

}


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2021.05.18 at 02:21:55 PM BST 
//


import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for record complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="record">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;choice>
 *         &lt;element name="transaction" type="{http://deutsche-boerse.com/DBRegHub}transactionPositionReport" maxOccurs="unbounded" minOccurs="0"/>
 *         &lt;element name="referencePartyDetails" type="{http://deutsche-boerse.com/DBRegHub}referencePartyDetails" maxOccurs="unbounded" minOccurs="0"/>
 *       &lt;/choice>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "record", propOrder = {
    "transaction",
    "referencePartyDetails"
})
public class Record {

    protected List<TransactionPositionReport> transaction;
    protected List<ReferencePartyDetails> referencePartyDetails;

    /**
     * Gets the value of the transaction property.
     * 
     * <p>
     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a <CODE>set</CODE> method for the transaction property.
     * 
     * <p>
     * For example, to add a new item, do as follows:
     * <pre>
     *    getTransaction().add(newItem);
     * </pre>
     * 
     * 
     * <p>
     * Objects of the following type(s) are allowed in the list
     * {@link TransactionPositionReport }
     * 
     * 
     */
    public List<TransactionPositionReport> getTransaction() {
        if (transaction == null) {
            transaction = new ArrayList<TransactionPositionReport>();
        }
        return this.transaction;
    }

    public List<ReferencePartyDetails> getReferencePartyDetails() {
        if (referencePartyDetails == null) {
            referencePartyDetails = new ArrayList<ReferencePartyDetails>();
        }
        return this.referencePartyDetails;
    }

}


import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType>
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="fileInformation" type="{http://deutsche-boerse.com/DBRegHub}fileInformation"/>
 *         &lt;element name="record" type="{http://deutsche-boerse.com/DBRegHub}record"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "fileInformation",
    "record"
})
@XmlRootElement(name = "reportFile")
public class ReportFile {

    @XmlElement(required = true)
    protected FileInformation fileInformation;
    @XmlElement(required = true)
    protected Record record;

    /**
     * Gets the value of the fileInformation property.
     * 
     * @return
     *     possible object is
     *     {@link FileInformation }
     *     
     */
    public FileInformation getFileInformation() {
        return fileInformation;
    }

    /**
     * Sets the value of the fileInformation property.
     * 
     * @param value
     *     allowed object is
     *     {@link FileInformation }
     *     
     */
    public void setFileInformation(FileInformation value) {
        this.fileInformation = value;
    }

    /**
     * Gets the value of the record property.
     * 
     * @return
     *     possible object is
     *     {@link Record }
     *     
     */
    public Record getRecord() {
        return record;
    }

    /**
     * Sets the value of the record property.
     * 
     * @param value
     *     allowed object is
     *     {@link Record }
     *     
     */
    public void setRecord(Record value) {
        this.record = value;
    }

}

仅供参考:在生成的 Java 类中,只有 java.lang.IllegalStateException: Marshaller must support the class of the marshalled object at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.batch.item.xml.StaxEventItemWriter.write(StaxEventItemWriter.java:767) ~[spring-batch-infrastructure-4.2.4.RELEASE.jar:4.2.4.RELEASE] at org.springframework.batch.item.xml.StaxEventItemWriter$$FastClassBySpringCGLIB$$d105dd1.invoke(<generated>) ~[spring-batch-infrastructure-4.2.4.RELEASE.jar:4.2.4.RELEASE] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.batch.item.xml.StaxEventItemWriter$$EnhancerBySpringCGLIB$$25cb8145.write(<generated>) ~[spring-batch-infrastructure-4.2.4.RELEASE.jar:4.2.4.RELEASE] at com.trax.europeangateway.itemwriter.omegaxml.OmegaXmlFileWriter.write(OmegaXmlFileWriter.java:37) ~[classes/:?] at com.trax.europeangateway.itemwriter.omegaxml.OmegaXmlFileWriter$$FastClassBySpringCGLIB$$48e7f3da.invoke(<generated>) ~[classes/:?] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE] 类使用 ReportFile 进行注释。但是如果我用 @XmlRootElement 注释 transaction 类,我可以生成一个 XML 文件,但是输出文件中的所有事务标记都带有命名空间信息。

@XmlRootElement

0 个答案:

没有答案