JAXB разбирает XML на неверный подкласс абстрактного типа - PullRequest
0 голосов
/ 11 июня 2019

У меня есть абстрактный класс 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;
}

Когда я передаю XML, соответствующий абстрактному классу A , но обладающий свойством b , яожидайте, что 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>

, но это не решает проблему.

Что я могу делать не так?Был бы признателен за любую помощь :)

РЕДАКТИРОВАТЬ: если это помогает, это метод, используемый для unmarshalling

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 голосов
/ 12 июня 2019

У меня есть решение, но оно использует композицию вместо наследования. Я не уверен, есть ли у вас причины, по которым вам нужно продлить класс.

Я упрощаю класс 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;
}

Вместо того, чтобы иметь Object в классе A, вы могли бы иметь интерфейс, который расширился бы как B, так и C.

Тогда было бы неправильно иметь значения <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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...