Search code examples
javaxmlxstream

How to deserialize same name node into two different classes with XStream


I have a problem while deserializing an XML file.

My file is like:

<mission>
    <branch>
        <alternative uid="0" type="ALT_MONITOR"/>
        <alternative uid="1" type="ALT_IF" condition="i==10"/>
    </branch>
</mission>

I have a class called Alternative:

public abtract class Alternative {
    @XStreamAsAttribute
    public int uid;
    @XStreamAsAttribute
    public String type;
}

This class is extended by two other class:

@XStreamAlias("alternative")
public class AlternativeA extends Alternative {
}

@XStreamAlias("alternative")
public class AlternativeB extends Alternative {
    @XStreamAsAttribute
    public String condition;
}

And then i have an xStream converter :

public class AlternativeConverter extends ReflectionConverter {
    public AlternativesConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
        super(mapper, reflectionProvider);
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
    if (reader.getAttribute("condition") != null) {
    AlternativeA alternativeA = new AlternativeA();
    alternativeA.setUid(Integer.parseInt(reader.getAttribute("uid")));
    alternativeA.setCondition(reader.getAttribute("condition"));
    return super.doUnmarshal(alternativeA, reader, context);
    }else {
    AlternativeB alternativeB = new AlternativeB();
    alternativeB.setUid(Integer.parseInt(reader.getAttribute("uid")));
    return super.doUnmarshal(alternativeB, reader, context);
    }
    }

     @SuppressWarnings("unchecked")
     @Override
     public boolean canConvert(Class clazz) {
        return Alternative.class.isAssignableFrom(clazz);
    }
}

But when i try to convert the xml to an object. When it reaches the alternative with a condition it throws an exception :

Cannot convert type AlternativeB to type AlternativeA

Do any of you have an idea or an int on what could cause that error ? Thank you in advance.


Solution

  • Java:

    package de.mosst.spielwiese;
    
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.junit.Test;
    
    import com.thoughtworks.xstream.XStream;
    import com.thoughtworks.xstream.annotations.XStreamAlias;
    import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
    import com.thoughtworks.xstream.converters.Converter;
    import com.thoughtworks.xstream.converters.UnmarshallingContext;
    import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
    import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
    import com.thoughtworks.xstream.io.HierarchicalStreamReader;
    import com.thoughtworks.xstream.mapper.Mapper;
    
    import lombok.Data;
    
    public class XStreamMultiClassesTest {
    
        @Test
        public void smokeTest() {
            InputStream file = XStreamMultiClassesTest.class.getResourceAsStream("XStreamMultiClassesTest.xml");
            XStream xStream = new XStream();
            xStream.ignoreUnknownElements();
            xStream.processAnnotations(Mission.class);
            xStream.processAnnotations(Alternative.class);
    
            Converter converter = new AlternativeConverter(xStream.getMapper(), xStream.getReflectionProvider());
            xStream.registerConverter(converter);
    
            Mission mission = (Mission) xStream.fromXML(file);
    
            System.out.println(mission);
            mission.branch.forEach(a -> {
                System.out.println(a.getClass());
                if (a instanceof AlternativeA) {
                    System.out.println("- condition: " + ((AlternativeA) a).condition);
                }
            });
        }
    
        public class AlternativeConverter extends ReflectionConverter {
    
            public AlternativeConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
                super(mapper, reflectionProvider);
            }
    
            @Override
            public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
                Alternative alternative = null;
                if (reader.getAttribute("condition") != null) {
                    alternative = new AlternativeA();
                    ((AlternativeA) alternative).condition = reader.getAttribute("condition");
                } else {
                    alternative = new AlternativeB();
                }
                alternative.uid = Integer.parseInt(reader.getAttribute("uid"));
                return super.doUnmarshal(alternative, reader, context);
            }
    
            @Override
            public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
                return Alternative.class.isAssignableFrom(clazz);
            }
        }
    
        @XStreamAlias("mission")
        @Data
        class Mission {
            public List<Alternative> branch = new ArrayList<>();
        }
    
        @XStreamAlias("alternative")
        @Data
        abstract class Alternative {
            @XStreamAsAttribute
            public int uid;
            @XStreamAsAttribute
            public String type;
        }
    
        class AlternativeA extends Alternative {
            public String condition;
        }
    
        class AlternativeB extends Alternative {
        }
    
    }
    

    XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <mission>
        <branch>
            <alternative uid="0" type="ALT_MONITOR" />
            <alternative uid="1" type="ALT_IF" condition="i==10" />
        </branch>
    </mission>