首页 > 解决方案 > Using JAXB, how can I unmarshal an XMLelement with an attribute-defined type to an object based on that attribute?

问题描述

My application is receiving an object payload marshalled into XML, e.g:

<targetedMessage>
   <sender>external application</sender>
   <payload class="class.path.from.external.application.Foo">
      <id>1</id>
   </payload>
</targetedMessage>

The payload class (Foo in example) can be one of a number of classes with a common inheritance structure.

Each has a corresponding class in my application:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo extends Baz {
    private Long id;
    //other fields, getters, setters
}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Bar extends Baz {
    private Long id;
    // other fields, getters, setters
}

and an enum can be used to find the local class of the object based on the foreign classpath:

public enum ForeignClass {

FOO("class.path.from.external.application.Foo", Foo.class),
BAR("class.path.from.external.application.Bar", Bar.class);

public static getClassFromForeignClassPath(String foreignClassPath) {
  // return class
}

I also have a TargetedMessage class to capture meta info regarding the message itself:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class TargetedMessage {
    private String sender;
    private Baz baz;
}

What annotation would be required to unmarshal the XML properly? I've tried using an XmlAdapter on the Baz field in the TargetedMessage class, but the ValueType parameter is always null.

@XmlAttribute doesn't appear as though it could work either, as the value of the class attributes are not the same as their corresponding local class names.

标签: javaxmljaxbunmarshalling

解决方案


你至少有两个选择。首先,您可以将“类”属性映射到字符串枚举,并在 TargetedMessage 的 #getBaz() 方法中实现实例化逻辑。XSD 如下所示(根据需要重命名类型):

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/XMLSchema" xmlns:tns="http://www.example.org/XMLSchema" elementFormDefault="qualified">

    <include schemaLocation=""></include>

    <simpleType name="simpleTypeClass">
        <restriction base="string">
            <enumeration value="class.path.from.external.application.Foo"></enumeration>
            <enumeration value="class.path.from.external.application.Bar"></enumeration>
        </restriction>
    </simpleType>

    <complexType name="payloadType">
        <sequence>
            <element name="id" type="string"></element>
        </sequence>
        <attribute name="class" type="tns:simpleTypeClass"></attribute>
    </complexType>

    <complexType name="targetedMessageType">
        <sequence>
            <element name="sender" type="string"></element>
            <element name="payload" type="tns:payloadType"></element>
        </sequence>
    </complexType>
</schema>

其次,您可以转换您的初始 XML,以便根据其“类”属性内容重命名“有效负载”元素。例如,它可以是“payloadFoo”、“payloadBar”等。然后,您可以将其直接映射到所需的类。相应的 XSD 如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/XMLSchema" xmlns:tns="http://www.example.org/XMLSchema" elementFormDefault="qualified">

    <simpleType name="simpleTypeFoo">
        <restriction base="string">
            <enumeration value="class.path.from.external.application.Foo"></enumeration>
        </restriction>
    </simpleType>

    <simpleType name="simpleTypeBar">
        <restriction base="string">
            <enumeration value="class.path.from.external.application.Bar"></enumeration>
        </restriction>
    </simpleType>

    <complexType name="payloadTypeFoo">
        <sequence>
            <element name="id" type="string"></element>
        </sequence>
        <attribute name="class" type="tns:simpleTypeFoo"></attribute>
    </complexType>

    <complexType name="payloadTypeBar">
        <sequence>
            <element name="id" type="string"></element>
        </sequence>
        <attribute name="class" type="tns:simpleTypeBar"></attribute>
    </complexType>

    <complexType name="payloadType">
        <choice>
            <element name="payloadFoo" type="tns:payloadTypeFoo" minOccurs="0" maxOccurs="1"></element>
            <element name="payloadBar" type="tns:payloadTypeBar" minOccurs="0" maxOccurs="1"></element>
        </choice>
    </complexType>

    <complexType name="targetedMessageType">
        <sequence>
            <element name="sender" type="string"></element>
            <element name="payload" type="tns:payloadType"></element>
        </sequence>
    </complexType>
</schema>

推荐阅读