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
java, xsd & marshalling: jre bug, my fault or xsd issues?
It's your fault :). Continue reading for the reason why.
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
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
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>