Search code examples
javajaxbjaxb2

Configuring collection of polymorphic objects to work in JAXB2


I'm switching from Castor to JAXB2 to perform marshaling/unmarshaling between XML and Java object. I'm having problem trying to configure a collection of polymorphic objects.

Sample XML

<project name="test project">
    <orange name="fruit orange" orangeKey="100" />
    <apple name="fruit apple" appleKey="200" />
    <orange name="fruit orange again" orangeKey="500" />
</project>

Project class

The oranges list works fine, I'm seeing 2 oranges in the list. But, I'm not sure how to configure fruitList. The fruitList should have 3 fruit: 2 oranges and 1 apple.

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Project {

    @XmlAttribute
    private String          name;

    @XmlElement(name = "orange")
    private List<Orange>    oranges     = new ArrayList<Orange>();

    // Not sure how to configure this... help!
    private List<Fruit>     fruitList   = new ArrayList<Fruit>();
}

Fruit class

Fruit is an abstract class. For some reason, defining this class as an abstract seems to be causing a lot of problems.

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class Fruit {

    @XmlAttribute
    private String  name;
}

Orange class

public class Orange extends Fruit {

    @XmlAttribute
    private String  orangeKey;
}

Apple class

public class Apple extends Fruit {

    @XmlAttribute
    private String  appleKey;
}

How do I configure my fruitList in Project to achieve what I want here?

Thanks much!


Solution

  • You want to leverage @XmlElementRef this is corresponds to the XML schema concept of substitution groups which corresponds to your question.

    Step 1 - Using @XmlElementRef

    The fruitList property is annotated with @XmlElementRef:

    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.XmlAttribute;
    import javax.xml.bind.annotation.XmlElementRef;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Project {
    
        @XmlAttribute
        private String name;
    
        @XmlElementRef
        private List<Fruit> fruitList = new ArrayList<Fruit>();
    
    }
    

    Step 2 - Annotate Apple and Orange with @XmlRootElement

    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Apple extends Fruit {
    
        @XmlAttribute
        private String  appleKey;
    
    }
    

    and

    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Orange extends Fruit {
    
        @XmlAttribute
        private String  orangeKey;
    
    }
    

    Demo Code

    The following code can be used to demonstrate the solution:

    import java.io.File;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Project.class, Apple.class, Orange.class);
    
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            Project project = (Project) unmarshaller.unmarshal(new File("input.xml"));
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(project, System.out);
        }
    
    }
    

    For More Information: