Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring collection of polymorphic objects to work in JAXB2

Tags:

java

jaxb

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!

like image 748
limc Avatar asked Feb 03 '11 15:02

limc


2 Answers

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:

  • http://bdoughan.blogspot.com/2010/11/jaxb-and-inheritance-using-substitution.html
like image 68
bdoughan Avatar answered Nov 10 '22 03:11

bdoughan


After futzing around... I think I got it working now:-

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

    @XmlAttribute
    private String      name;

    // this has to be commented out, or else oranges won't show up in fruitList
    // @XmlElement(name = "orange")
    // private List<Orange> oranges = new ArrayList<Orange>();

    @XmlElements({
            @XmlElement(name = "orange", type = Orange.class),
            @XmlElement(name = "apple", type = Apple.class)
    })
    private List<Fruit> fruitList   = new ArrayList<Fruit>();

}
like image 31
limc Avatar answered Nov 10 '22 03:11

limc