Search code examples
javaxmltreexstreamxml-deserialization

Using XStream to create a list of different objects


I'm creating a program in Java which contains a tree-structure of objects. All classes involved implement the same interface, and each class contains a list of children:

public class MyClass1 implements MyInterface {    
    List<MyInterface> children;
}

public class MyClass2 implements MyInterface {    
    List<MyInterface> children;
}

public class MyClass3 implements MyInterface {    
    List<MyInterface> children;
}

....

Now the three structure is defined in an XML-file:

<myclass1>
    <myclass2></myclass2>
    <myclass1>
        <myclass3></myclass3>
    </myclass1>
</myclass1>

The tree structure can be of any type, defined by the user. Of course, each class contains class-specific variables which I omitted for now.

Now I'm trying to use XStream to deserialize the XML-file to Java-objects, preferably using annotations, but I don't know how to do this. I don't want to end up making a list for each possible class, e.g. in MyClass1:

@XStreamImplicit(itemFieldName="myclass2")
List<MyClass2> children2;
@XStreamImplicit(itemFieldName="myclass3")
List<MyClass3> children3;
@XStreamImplicit(itemFieldName="myclass4")
List<MyClass4> children4;

Any suggestions on how to solve this case with XStream? Or should I use other technologies?


Solution

  • source.xml (Containing the XML we want to load):

    <myclass1>
        <myclass2></myclass2>
        <myclass1>
            <myclass3></myclass3>
        </myclass1>
    </myclass1>
    

    Interface:

    import java.util.List;
    
    public interface Myinterface {
        List<? extends Myinterface> getChildren();
    }    
    

    Abstract bean class (declaring children with @XStreamImplicit) :

    import java.util.ArrayList;
    import java.util.List;
    
    import com.thoughtworks.xstream.annotations.XStreamImplicit;
    
    public class MyAbstract implements Myinterface {
        @XStreamImplicit
        private List<? extends Myinterface> children = new ArrayList<Myinterface>();
    
    
        public List<? extends Myinterface> getChildren() {
            return children;
        }
    }
    

    Bean classes (extending MyAbstract, using @XStreamAlias to use the XML string you need):

    import com.thoughtworks.xstream.annotations.XStreamAlias;
    
    @XStreamAlias(value="myclass1")
    public class Myclass1 extends MyAbstract {
    
    }
    
    @XStreamAlias(value="myclass2")
    public class Myclass2 extends MyAbstract {
    
    }
    
    @XStreamAlias(value="myclass3")
    public class Myclass3 extends MyAbstract {
    
    }
    

    Main class (reading your XML source file. Don't forget processAnnotations to load configuration from Annotated classes):

    import com.thoughtworks.xstream.XStream;
    
    public class XStreamTest {
        public static void main(String[] args) {
            XStream xStream = new XStream();
            xStream.processAnnotations(Myclass1.class);
            xStream.processAnnotations(Myclass2.class);
            xStream.processAnnotations(Myclass3.class);
    
            Object fromXML = xStream.fromXML(XStreamTest.class.getResourceAsStream("source.xml"));
            System.out.println(fromXML);
        }
    }