Search code examples
javaspringspring-bootspring-batchxstream

XStream XStreamMarshaller CannotResolveClassException java.util.ArrayList


I have to read multiple XML files using Spring Batch.

I'm using XStream and i'm struggling to read ArrayList Of objects.

Error stack :

at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.4.3.jar:2.4.3]
Caused by: com.thoughtworks.xstream.converters.ConversionException: 
---- Debugging information ----
cause-exception     : com.thoughtworks.xstream.mapper.CannotResolveClassException
cause-message       : workCompany
class               : java.util.ArrayList
required-type       : java.util.ArrayList
converter-type      : com.thoughtworks.xstream.converters.collections.CollectionConverter
path                : /person/works/Work/workCompany
line number         : 15
class[1]            : com.oualid.xmltojava.model.Works
converter-type[1]   : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
class[2]            : com.oualid.xmltojava.model.Person
version             : 5.3.4
-------------------------------
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:79) ~[xstream-1.4.9.jar:1.4.9]

My POJOs :

package com.oualid.xmltojava.model;


import com.thoughtworks.xstream.annotations.XStreamAlias;


import java.util.List;

@XStreamAlias("Person")
public class Person {
    @XStreamAlias("personId")
    private Integer personId;
    @XStreamAlias("firstName")
    private String firstName;
    @XStreamAlias("lastName")
    private String lastName;
    @XStreamAlias("email")
    private String email;
    @XStreamAlias("age")
    private Integer age;
    @XStreamAlias("contactDetails")
    private List<String> contactDetails;
    @XStreamAlias("works")
    private Works works;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Integer getPersonId() {
        return personId;
    }
    public void setPersonId(Integer personId) {
        this.personId = personId;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }

    public List<String> getContactDetails() {
        return contactDetails;
    }

    public void setContactDetails(List<String> contactDetails) {
        this.contactDetails = contactDetails;
    }

    public Works getWorks() {
        return works;
    }

    public void setWorks(Works works) {
        this.works = works;
    }
}

package com.oualid.xmltojava.model;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamImplicit;

import java.util.List;

@XStreamAlias("works")
public class Works {

   @XStreamImplicit(itemFieldName = "Work")
    private List<Work> Work;

    public List<Work> getWorks() {
        return Work;
    }

    public void setWorks(List<Work> works) {
        this.Work = works;
    }
}

package com.oualid.xmltojava.model;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("Work")
public class Work {

    @XStreamAlias("workCompany")
    private String workCompany;
    @XStreamAlias("workMission")
    private String workMission;

    public String getWorkCompany() {
        return workCompany;
    }

    public void setWorkCompany(String workCompany) {
        this.workCompany = workCompany;
    }

    public String getWorkMission() {
        return workMission;
    }

    public void setWorkMission(String workMission) {
        this.workMission = workMission;
    }
}

One of my XML file :

<?xml version="1.0" encoding="UTF-8"?>
<persons>
    <person>
        <personId>1</personId>
        <firstName>PETER</firstName>
        <lastName>SCHMIDT</lastName>
        <email>[email protected]</email>
        <contactDetails>
            <string>contact1</string>
            <string>contact2</string>
        </contactDetails>
        <age>28</age>
        <works>
            <Work>
                <workCompany>ESI</workCompany>
                <workMission>Java Developper</workMission>
            </Work>
            <Work>
                <workCompany>ESI</workCompany>
                <workMission>JS Developper</workMission>
            </Work>
        </works>

    </person>
</persons>

Ther reader

@Bean
    public StaxEventItemReader<Person> reader(){
        StaxEventItemReader<Person> reader = new StaxEventItemReader<Person>();
        //reader.setResource(new ClassPathResource("persons.xml"));
        reader.setFragmentRootElementName("person");


        Map<String,String> aliasesMap =new HashMap<String,String>();
        aliasesMap.put("person", "com.oualid.xmltojava.model.Person");
        aliasesMap.put("works", "com.oualid.xmltojava.model.Works");
        aliasesMap.put("Work", "com.oualid.xmltojava.model.Work");
        XStreamMarshaller marshaller = new XStreamMarshaller();
        marshaller.setAliases(aliasesMap);

        reader.setUnmarshaller(marshaller);



        return reader;
    }

For information, this reader is passed into multiResourceItemReader and it works just fine if i don't have <works></works> in my XML file


Solution

  • I post the response if any one else will face the same problem :

    even when i added the @XStreamImplicit(itemFieldName = "Work") i find out that the marshaller is not aware of this annotation.

    So there is two possible solutions for me :

    1- @Mahmoud Ben Hassine solution above.

    2- Simply activate the auto detect annotation to true

    XStreamMarshaller marshaller = new XStreamMarshaller();
    marshaller.setAutodetectAnnotations(true);
    

    And of course don't forget to add the annotation for your List :

    @XStreamImplicit(itemFieldName = "Work")
    private List<Work> Work;
    

    I prefered the second one because i have a really complexe XML to read with a lot of object collections, so annotations wil be clean i guess.

    Happy coding :)