JAXB: Concrete type information (xsi:type) missing

2020-07-13 13:01发布

I'm generating JAXB classes from XSDs for a SOAP WebService I'm building a client for (generated with jaxws-maven-plugin v2.4.1, wsimport goal).

I'm having a problem where JAXB will not add xsi:type-Information to a node that is of an abstract type when marshalling my objects. The WebService now (rightfully, I think) complains that I'm trying to pass it elements without specifying of what type they are ("The type definition cannot be abstract for element...").

Here's a simplified example that demonstrates my issue:

Abstract type schema: (abstract.xsd)

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.example.com/namespace_abstract"
            elementFormDefault="qualified"
            attributeFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com/namespace_abstract">
    <xsd:complexType name="ElementType" abstract="true">
        <xsd:simpleContent>
            <xsd:extension base="xsd:string"/>
        </xsd:simpleContent>
    </xsd:complexType>
</xsd:schema>

Concrete type schema: (concrete.xsd)

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.example.com/namespace_concrete"
            elementFormDefault="qualified"
            attributeFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com/namespace_concrete"
            xmlns:abstr="http://www.example.com/namespace_abstract">
    <xsd:import namespace="http://www.example.com/namespace_abstract" schemaLocation="abstract.xsd"/>

    <!-- Concrete type -->
    <xsd:complexType name="ElementTypeExtension">
        <xsd:simpleContent>
            <xsd:restriction base="abstr:ElementType">
                <xsd:enumeration value="one"/>
                <xsd:enumeration value="two"/>
                <xsd:enumeration value="three"/>
            </xsd:restriction>
        </xsd:simpleContent>
    </xsd:complexType>

    <!-- Type that has a field of the abstract type -->
    <xsd:complexType name="ExtensionTypeContainer">
        <xsd:sequence>
            <xsd:element name="type" type="abstr:ElementType"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

Test:

import com.example.namespace_concrete.*;

import javax.xml.bind.*;
import javax.xml.bind.annotation.XmlRootElement;

public class MarshallingTest {
    public static void main(String[] args) throws JAXBException {
        ElementTypeExtension elementTypeExtension = new ElementTypeExtension();
        elementTypeExtension.setValue("one");
        ExtensionTypeContainer extensionTypeContainer = new ExtensionTypeContainer();
        extensionTypeContainer.setType(elementTypeExtension);

        XmlRoot xmlRoot = new XmlRoot();
        xmlRoot.setContainer(extensionTypeContainer);

        Marshaller marshaller = JAXBContext.newInstance(XmlRoot.class, ExtensionTypeContainer.class).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(xmlRoot, System.out);
    }

    @XmlRootElement
    static class XmlRoot {
        private ExtensionTypeContainer container;
        public ExtensionTypeContainer getContainer() { return container; }
        public void setContainer(ExtensionTypeContainer container) { this.container = container; }
    }
}

Output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xmlRoot xmlns:ns2="http://www.example.com/namespace_concrete">
    <container>
        <ns2:type>one</ns2:type>
    </container>
</xmlRoot>

The missing part is the xsi:type="ns2:ElementTypeExtension" on the ns2:type node, leaving the type definition ambiguous (of course in my example there's only one concrete type, but still).

I've found a way to generate the xsi:type by modifiying the generated source code of the abstract class (removed JAXB-javadoc for readability):

package com.example.namespace_abstract;

import javax.xml.bind.annotation.*;
import com.example.namespace_concrete.ElementTypeExtension;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ElementType", propOrder = { "value" })
@XmlSeeAlso({ ElementTypeExtension.class })
public abstract class ElementType {
    @XmlValue
    protected String value;
    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }
}

Once I remove the @XmlValue-Annotation from the value field the xsi:type-Information is present in the marshalled XML. Source code is generated from the XSDs though, so this isn't really an option.

Can anybody please give me an idea on how to get the xsi:type in there? Modifying the xsd would be an option.

标签: java xml xsd jaxb
1条回答
对你真心纯属浪费
2楼-- · 2020-07-13 13:41

Updating to JAXB 2.2.11 fixed the issue, seems to be related to this JAXB issue.

查看更多
登录 后发表回答