java, xsd & marshalling: jre bug, my fault or xsd issues?

前端 未结 1 1758
没有蜡笔的小新
没有蜡笔的小新 2021-01-17 16:46

I am running into a problem while marshalling a JAXB model tree to a xml file.

I created those model classes using xjc. I c

相关标签:
1条回答
  • 2021-01-17 17:44

    TL;DR

    java, xsd & marshalling: jre bug, my fault or xsd issues?

    It's your fault :). Continue reading for the reason why.


    Java Model

    Below is a Java model that is based on the one hinted at in your question that can be used to produce the same exception that you are seeing.

    Root

    Below is a root object that has one property. The type of that property has two subclasses. One of the subclasses (B) corresponds to a named type, and the other (A) to an anonymous type.

    package com.mypackage;
    
    import javax.xml.bind.annotation.XmlRootElement;
    @XmlRootElement
    public class Root {
    
        private BaseType baseType;
    
        public BaseType getBaseType() {
            return baseType;
        }
    
        public void setBaseType(BaseType baseType) {
            this.baseType = baseType;
        }
    
    }
    

    BaseType

    This is the super class. It has two subclasses: A and B.

    package com.mypackage;
    
    import javax.xml.bind.annotation.XmlSeeAlso;
    
    @XmlSeeAlso({A.class, B.class})
    public class BaseType {
    
    }
    

    A

    The A class corresponds to an anonymous complex type. An anonymous type is represented with the @XmlType(name="") annotation.

    package com.mypackage;
    
    import javax.xml.bind.annotation.XmlType;
    
    @XmlType(name="")
    public class A extends BaseType {
    
    }
    

    B

    The B class corresponds to the named complex type b. The default is the same as having @XmlType(name="b").

    package com.mypackage;
    
    public class B extends BaseType {
    
    }
    

    Demo Code - That Reproduces Error

    Demo

    When the baseType property on the Root class is one of the subclasses then the xsi:type attribute is added to qualify the element (similar to a cast in Java). The instance is required to correspond to a named complex type. In this case A does not.

    package com.mypackage;
    
    import javax.xml.bind.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            Root root = new Root();
            BaseType baseType = new A(); // Invalid Value
            root.setBaseType(baseType);
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(root, System.out);
        }
    
    }
    

    Exception

    This results in the following exception. As stated by the exception A is not a valid substitute for BaseType because it corresponds to an anonymous complex type, since since it corresponds to an anonymous type it doesn't have the necessary value for the xsi:type attribute.

    Exception in thread "main" javax.xml.bind.MarshalException
     - with linked exception:
    [com.sun.istack.internal.SAXException2: Instance of "com.mypackage.A" is substituting "com.mypackage.BaseType", but "com.mypackage.A" is bound to an anonymous type.]
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:311)
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236)
        at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
        at com.mypackage.Demo.main(Demo.java:16)
    Caused by: com.sun.istack.internal.SAXException2: Instance of "com.mypackage.A" is substituting "com.mypackage.BaseType", but "com.mypackage.A" is bound to an anonymous type.
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:237)
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:652)
        at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:143)
        at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:343)
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:582)
        at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:325)
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:483)
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308)
        ... 3 more
    

    Demo Code - That Works

    Demo

    If instead of A you specified the other subclass B which corresponds to a named complex type.

    package com.mypackage;
    
    import javax.xml.bind.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            Root root = new Root();
            BaseType baseType = new B(); // Valid Value
            root.setBaseType(baseType);
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(root, System.out);
        }
    
    }
    

    Output

    Then you will see everything works as expected an the type name is included when the baseType property is marshalled.

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <root>
        <baseType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="b"/>
    </root>
    
    0 讨论(0)
提交回复
热议问题