JAXB 将子类的实例解组到列表中

JAXB unmarshal instances of subclasses into List

假设我想用 xml 表示算术表达式,所以我有:

@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;
    }
}

和我的测试用例:

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());

    }
}

这里的问题是非Evaluable存储在Add中的ArrayList中,原因是类型擦除,JAXB不知道元素是哪种类型应该反映到,所以在 Add.

的 eval 中会有一个转换错误

我通过以下方式进行了测试:

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

这将得到解决。

但我不想将此 table 传播到全世界。如果我想附加一些子类型,事情就会变得一团糟。

所以我的问题是哪个注释适合我的情况table?或任何输入解组器的系统?

我正在使用 glassfish 的 JAXB,但我无法选择要使用的实现。

您可以使用 @XmlElementRef 来获得您正在寻找的行为:

@XmlElementRef
private ArrayList<Evaluable> elems;

更新

it says Evaluable or any of its subclasses are not known to this context

啊,没错,因为您已将 Evaluable 标记为 @XmlTransient,JAXB 忽略它并且不认为它存在。您可能添加了它,因为当您没有它时,您会遇到以下异常。

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