Java 为什么Jaxb2Marshaller中需要检查根元素?

Java 为什么Jaxb2Marshaller中需要检查根元素?,java,spring,xsd,jaxb,marshalling,Java,Spring,Xsd,Jaxb,Marshalling,我使用Jaxb2Marshaller通过spring@ResponseBody注释封送Java bean。对于JSON,封送处理工作正常。但对于xml,我不断得到HTTP406响应。在Jaxb2Marshaller类中稍微挖掘一下,就会发现它检查有界类的@XmlRootElement(见下面的代码片段) 从xsd生成java代码时,我的pojo不包含@XmlRootElement,并且AnnotationMethodHandlerAdapter未识别正确的消息转换器,最终导致406 我没有从xs

我使用Jaxb2Marshaller通过spring@ResponseBody注释封送Java bean。对于JSON,封送处理工作正常。但对于xml,我不断得到HTTP406响应。在Jaxb2Marshaller类中稍微挖掘一下,就会发现它检查有界类的@XmlRootElement(见下面的代码片段)

从xsd生成java代码时,我的pojo不包含@XmlRootElement,并且AnnotationMethodHandlerAdapter未识别正确的消息转换器,最终导致406

我没有从xsd自动生成java代码,而是创建了自己的pojo类并使用@XmlRootElement。那么编组就可以了

我想了解为什么对有界类进行@XmlRootElement检查很重要。或者在xsd中将元素指定为@XmlRootElement的任何方法

来自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;
}
公共布尔支持(类clazz){
返回内部支持(clazz,true);
}
私有布尔支持内部(类clazz,布尔checkForXmlRootElement){
if(checkForXmlRootElement&&clazz.getAnnotation(XmlRootElement.class)==null){
返回false;
}
if(clazz.getAnnotation(XmlType.class)==null){
返回false;
}
if(StringUtils.hasLength(getContextPath())){
字符串className=ClassUtils.getQualifiedName(clazz);
int lastDotIndex=className.lastIndexOf('.');
如果(lastDotIndex==-1){
返回false;
}
字符串packageName=className.substring(0,lastDotIndex);
String[]ContextPath=StringUtils.tokenizeToStringArray(getContextPath(),“:”);
for(字符串上下文路径:上下文路径){
if(contextPath.equals(packageName)){
返回true;
}
}
返回false;
}
如果(!ObjectUtils.isEmpty(classesToBeBound))为else{
返回Arrays.asList(classestobebund).contains(clazz);
}
返回false;
}
编辑:
Blaise answer帮助我解决了@XmlRootElement问题。但是,如果有人知道为什么需要检查XmlRootElement,这将是一个很好的信息。

为什么要检查@XmlRootElement注释

将对象编组为XML时,Spring需要根元素。JAXB提供了两种机制来实现这一点:

  • @XmlRootElement注释
  • 在JAXBElement实例中包装根对象
  • 由于对象没有包装在JAXBElement中,因此Spring确保满足其他条件

    如何生成@XmlRootElement

    JAXB将为XML模式中的所有全局元素生成@XmlRootElement注释。以下内容将导致@XmlElement:

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       <xsd:element name="foo">
           <xsd:complexType>
               ...
           </xsd:complextType>
        </xsd:element>
    </xsd:schema>
    
    
    ...
    
    未生成@XmlRootElement时

    不会为全局类型生成@XmlRootElement注释

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       <xsd:element name="foo" type="foo"/>
       <xsd:complexType name="foo">
           ...
       </xsd:complexType>
    </xsd:schema>
    
    
    ...
    
    相反,与全局类型关联的全局元素以@XmlElementDecl注释的形式在ObjectFactory类(用@XmlRegistry注释)中捕获。这些注释

    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);
        }
    
    }
    
    生成包;
    导入javax.xml.bind.JAXBElement;
    导入javax.xml.bind.annotation.xmlementdecl;
    导入javax.xml.bind.annotation.XmlRegistry;
    导入javax.xml.namespace.QName;
    @XmlRegistry
    公共类对象工厂{
    私有最终静态QName_Foo_QName=新QName(“,“Foo”);
    public Foo createFoo(){
    返回新的Foo();
    }
    @xmlementdecl(名称空间=“name=”foo”)
    公共JAXBElement createFoo(Foo值){
    返回新的JAXBElement(_Foo_QNAME,Foo.class,null,value);
    }
    }
    

    @XmlElementDecl注释提供了与@XmlRootElement类似的信息,可用于解组操作。JAX-RS实现可能不会利用@XmlElementDecl,但是因为封送处理操作需要将对象包装在一个JAXBElement对象中,以提供根元素名称/命名空间。

    您可以将JAXBElement用于没有@XmlRootElement注释的类@如果您是从xsd生成代码,则XmlRootElement注释仅放置到未引用的顶级对象

    Edit
    
    See @Blaise Doughan answer.
    
    @XmlRootElement will be placed only if there is no reference to that type in another type.
    
    这是一个众所周知的问题:


    “在Jaxb2Marshaller中检查@XmlRootElement注释应该是可选的”

    谢谢,现在我可以生成POJO,其中@XmlRootElement按照您的指定更改xsd。@XmlRootElement是为与全局元素对应的所有生成的类添加的,不管这些元素是否被引用。我已经更新了我的答案,希望这有助于澄清问题。请在你的答案中包含更多的链接。如果链接断开,这篇文章对未来的用户没有帮助。