JAXB unmarshal instances of subclasses into a list

say I want to represent arithmetic expressions with xml, so I have:

@XmlRootElement
@XmlTransient
@XmlSeeAlso({Num.class, Add.class})
public abstract class Evaluable
{
    public abstract int eval();
}

@XmlRootElement
@XmlType(name = "num")
public class Num extends Evaluable
{
    @XmlValue
    private int val;

    @Override
    public int eval()
    {
        return val;
    }
}

@XmlRootElement
@XmlType
public class Add extends Evaluable
{
    @XmlAnyElement
    private ArrayList<Evaluable> elems;

    @Override
    public int eval()
    {
        int sum = 0;
        for (Evaluable elem : elems)
        {
            sum += elem.eval();
        }
        return sum;
    }
}

      

and my test case:

public class RecursiveUnmarshalTest
{
    @Test
    public void testAdd() throws Exception
    {
        String xml = "<add><num>10</num><num>20</num></add>";
        assertEquals(30,
                ((Evaluable) JAXBContext.newInstance(Evaluable.class).createUnmarshaller().unmarshal(new StringReader(
                        xml))).eval());

    }
}

      

the problem is that it Evaluable

is not stored in ArrayList

in Add

, and the reason is type erasure, JAXB does not know what type the elements should reflect to, so there will be a fill error in eval Add

.

and I have verified that:

public class Add extends Evaluable
{
    @XmlElements({
            @XmlElement(name = "num", type = Num.class),
            @XmlElement(name = "add", type = Add.class)
    })
    private ArrayList<Evaluable> elems;
...

      

this will be fixed.

but I don't want to distribute this spreadsheet all over the world. if i want to add some subtypes it gets messy.

so my question is which annotation is appropriate for my case? or any system for input to unmarshaller?

I am using JAXB glassfish and I could not choose an implementation to use.

+3


source to share


1 answer


You can use @XmlElementRef

to get the behavior you are looking for:

@XmlElementRef
private ArrayList<Evaluable> elems;

      


UPDATE



it says Evaluable or any of its subclasses is not aware of this context

Ahh, that's correct, because you marked Evaluable

as @XmlTransient

, JAXB ignores it and doesn't think it exists. You probably added it because when you don't have it, you get the following exception.

Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Invalid @XmlElementRef : Type "class forum27826242.Evaluable" or any of its subclasses are not known to this context.
    this problem is related to the following location:
        at private java.util.ArrayList forum27826242.Add.elems
        at forum27826242.Add
        at @javax.xml.bind.annotation.XmlSeeAlso(value=[class forum27826242.Num, class forum27826242.Add])
        at forum27826242.Num

      

+1


source







All Articles