JAXB将XML解组为抽象类型的错误子类

时间:2019-06-11 16:42:27

标签: java xml jaxb

我有一个抽象类 A ,其中有两个子类 B C A 看起来像这样:

@XmlTransient
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "A", propOrder = {
    "operation",
    "origin",
})
@XmlSeeAlso({B.class, C.class})
public abstract class A {

    @XmlElement(required = true)
    protected Operation operation;

    @XmlElement(required = true)
    protected Origin origin;

    // getters and setters
}

B

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "A") // root element is A for all subclasses
@XmlType(name = "B", propOrder = {
    "operation",
    "origin",
    "b",
})
public class B extends A {

    @XmlElement(required = true)
    protected String b;

    // getter and setter
}

C

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "A") // root element is A for all subclasses.
@XmlType(name = "C", propOrder = {
    "operation",
    "origin",
    "c",
})
public class C extends A {

    @XmlElement(required = true)
    protected String c;

    // getter and setter
}

我如何创建JAXB上下文:

private static JAXBContext createJaxbContext() throws JAXBException {
    if(jaxbContext == null) {
        final ClassLoader classLoader = A.class.getClassLoader();
        final String contextPath = A.class.getPackage().getName();
        jaxbContext = JAXBContext.newInstance(contextPath, classLoader);
    }
    return jaxbContext;
}

当我传入符合抽象类 A 但具有属性 b 的XML时,我希望JAXB能够将其解组为类型的对象> B (如果存在属性 c ,则与 C 相同)。当前正在发生的情况是,它尝试将所有XML字符串解组到 C 且仅解封为 C ,从而导致ClassCastException。

我尝试将子类指定为根节点上的类型-例如:

<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="C">
    <operation id="123" action="insert"/>
    <origin>
        <environment>DEV</environment>
        <instance>root</instance>
    </origin>
    <c>c value</c>
</A>

但这不能解决问题。

我可能做错了什么?将不胜感激:)

编辑:如果有帮助,这是用于解组的方法

public static A toA(String xml) throws Exception{
    try {
      final Unmarshaller jaxbUnmarshaller = getJaxbContext().createUnmarshaller();
      final StringReader reader = new StringReader(xml);
      return (A) jaxbUnmarshaller.unmarshal(reader);
    } catch (JAXBException e) {
      throw new Exception("Failed to unmarshal A " + "[" + e.toString() + "].");
    }
  }

这是我的ObjectFactory:

@XmlRegistry
public class ObjectFactory {

    private final static QName A_QNAME = new QName("", "A");

    public ObjectFactory() {}

    public B createB() {
        return new B();
    }

    public C createC() {
        return new C();
    }

    public Operation createOperation() {
        return new Operation();
    }

    public Origin createOrigin() {
        return new Origin();
    }

    public A createA(A value) {
        return new JAXBElement<>(A_QNAME, A.class, null, value).getValue();
    }
}

1 个答案:

答案 0 :(得分:0)

我有一个解决方案,但是它使用组合而不是继承。我不确定您是否有必要扩展该类。

我简化了A类,因此我不必为起源和操作创建类,但是我相信您已经明白了(如果没有请告诉我):

@XmlAccessorType(XmlAccessType.FIELD)
public class A {

    // operation and origin stay the same

    @XmlElements({
            @XmlElement(name = "b", type = B.class),
            @XmlElement(name = "c", type = C.class),
    })
    protected Object bOrC;
}

B类和C类看起来像这样:

@XmlAccessorType(XmlAccessType.FIELD)
public class B {

    @XmlValue
    private String value;
}

您可以拥有一个B和C都可以扩展的接口,而不是在A类中拥有Object。

然后将其解编为具有<c><b>值就可以了:

<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <!--<operation id="123" action="insert"/>-->
    <!--<origin>-->
        <!--<environment>DEV</environment>-->
        <!--<instance>root</instance>-->
    <!--</origin>-->
    <c>c value</c>
</A>