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.
source to share
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
source to share