JAXB将子类的实例解组为List

时间:2015-01-07 18:33:28

标签: java xml jaxb

说我想用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存储在ArrayList Add中,原因是类型擦除,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;
...

这将被修复。

但我不想在世界各地传播这张桌子。如果我想附加一些子类型,事情就会变得混乱。

所以我的问题是哪种注释适合我的情况?或任何输入unmarshaller的系统?

我正在使用glassfish的JAXB,我无法选择使用。

1 个答案:

答案 0 :(得分:1)

您可以使用@XmlElementRef来获取您要查找的行为:

@XmlElementRef
private ArrayList<Evaluable> elems;

更新

  

它表示可以使用Evaluable或其任何子类   上下文

啊,这是正确的,因为你将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