I am using Jaxb2Marshaller to marshal Java beans through spring @ResponseBody annotation. For JSON marshaling was working fine. But for xml I was continuously getting HTTP 406 response. Little bit digging in Jaxb2Marshaller class reveals it checks for @XmlRootElement for bounded classes (see below snippet).
While generating java code from xsd my pojo does not contain @XmlRootElement and proper message converter was not identified by AnnotationMethodHandlerAdapter and finally result in 406.
Instead of auto generating java code from xsd I have created my own pojo class and used @XmlRootElement. Then marshaling works fine.
I want to understand why it is important of having @XmlRootElement check for bounded classes. Or any way to specify element as @XmlRootElement in xsd.
Code snippet from Jaxb2Marshaller:
public boolean supports(Class clazz) {
return supportsInternal(clazz, true);
}
private boolean supportsInternal(Class<?> clazz, boolean checkForXmlRootElement) {
if (checkForXmlRootElement && clazz.getAnnotation(XmlRootElement.class) == null) {
return false;
}
if (clazz.getAnnotation(XmlType.class) == null) {
return false;
}
if (StringUtils.hasLength(getContextPath())) {
String className = ClassUtils.getQualifiedName(clazz);
int lastDotIndex = className.lastIndexOf('.');
if (lastDotIndex == -1) {
return false;
}
String packageName = className.substring(0, lastDotIndex);
String[] contextPaths = StringUtils.tokenizeToStringArray(getContextPath(), ":");
for (String contextPath : contextPaths) {
if (contextPath.equals(packageName)) {
return true;
}
}
return false;
}
else if (!ObjectUtils.isEmpty(classesToBeBound)) {
return Arrays.asList(classesToBeBound).contains(clazz);
}
return false;
}
Edit: Blaise answer helped me solve @XmlRootElement problem. But still if someone has any information about why check for XmlRootElement is required, will be a good info.
Why the @XmlRootElement Annotation is Checked For
Spring requires a root element when marshalling the object to XML. JAXB provides two mechanisms to do this:
Since the object is not wrapped in a JAXBElement Spring is ensuring that the other condition is met.
How Generate an @XmlRootElement
JAXB will generate an @XmlRootElement annotation for all global elements in an XML schema. The following will cause an @XmlElement:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="foo">
<xsd:complexType>
...
</xsd:complextType>
</xsd:element>
</xsd:schema>
When @XmlRootElement is not Generated
An @XmlRootElement annotation will not be generated for global types.
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="foo" type="foo"/>
<xsd:complexType name="foo">
...
</xsd:complexType>
</xsd:schema>
Instead the global element(s) associated with the global types are captured in the ObjectFactory class (annotated with @XmlRegistry) in the form of @XmlElementDecl annotations. These annotations
package generated;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
@XmlRegistry
public class ObjectFactory {
private final static QName _Foo_QNAME = new QName("", "foo");
public Foo createFoo() {
return new Foo();
}
@XmlElementDecl(namespace = "", name = "foo")
public JAXBElement<Foo> createFoo(Foo value) {
return new JAXBElement<Foo>(_Foo_QNAME, Foo.class, null, value);
}
}
The @XmlElementDecl annotation provides similar information as @XmlRootElement and could be used for unmarshal operations. JAX-RS implementations probably do not leverage @XmlElementDecl however since marshal operations would require the object to be wrapped in a JAXBElement object to provide the root element name/namespace.
it's an known issue: https://jira.springsource.org/browse/SPR-7931
"Checking for @XmlRootElement annotation should be made optional in Jaxb2Marshaller"
You can use JaxbElement for classes that does not have @XmlRootElement annotation. @XmlRootElement annotation is placed only to non referenced top level objects if you are generating your code from xsd
Edit
See @Blaise Doughan answer.
@XmlRootElement will be placed only if there is no reference to that type in another type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With