Почему в Jaxb2Marshaller требуется проверка корневого элемента?

Я использую Jaxb2Marshaller для маршалинга Java-бинов с помощью весенней аннотации @ResponseBody. Маршалинг JSON работал нормально. Но для xml я постоянно получал ответ HTTP 406. Небольшое копание в классе Jaxb2Marshaller показывает, что он проверяет @XmlRootElement для ограниченных классов (см. ниже фрагмент).

При генерации java-кода из xsd мой pojo не содержит @XmlRootElement, а правильный преобразователь сообщений не был идентифицирован AnnotationMethodHandlerAdapter и, наконец, приводит к 406.

Вместо автоматического создания кода Java из xsd я создал свой собственный класс pojo и использовал @XmlRootElement. Тогда маршалинг работает нормально.

Я хочу понять, почему важно иметь проверку @XmlRootElement для ограниченных классов. Или любой способ указать элемент как @XmlRootElement в xsd.

Фрагмент кода от 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;
}

Изменить: ответ Блейза помог мне решить проблему @XmlRootElement. Но все же, если у кого-то есть какая-либо информация о том, почему требуется проверка XmlRootElement, будет хорошей информацией.


person Jaydeep Patel    schedule 26.01.2011    source источник
comment
Я обновил свой ответ, надеюсь, это поможет прояснить ситуацию.   -  person bdoughan    schedule 27.01.2011


Ответы (3)


Почему проверяется аннотация @XmlRootElement

Spring требует корневого элемента при маршаллинге объекта в XML. JAXB предоставляет для этого два механизма:

  1. Аннотация @XmlRootElement
  2. Обертывание корневого объекта экземпляром JAXBElement.

Поскольку объект не заключен в JAXBElement, Spring гарантирует выполнение другого условия.

Как создать @XmlRootElement

JAXB создаст аннотацию @XmlRootElement для всех глобальных элементов в схеме XML. Следующее вызовет @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>

Вместо этого глобальные элементы, связанные с глобальными типами, фиксируются в классе ObjectFactory (с аннотацией @XmlRegistry) в форме аннотаций @XmlElementDecl. Эти аннотации

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);
    }

}

Аннотация @XmlElementDecl предоставляет информацию, аналогичную @XmlRootElement, и может использоваться для неупорядочивающих операций. Реализации JAX-RS, вероятно, не используют @XmlElementDecl, однако, поскольку маршалирующие операции потребуют, чтобы объект был обернут в объект JAXBElement для предоставления имени/пространства имен корневого элемента.

person bdoughan    schedule 26.01.2011
comment
Спасибо, теперь я могу сгенерировать POJO с элементом @XmlRootElement, изменяющим xsd, как вы указали. - person Jaydeep Patel; 27.01.2011

это известная проблема: https://jira.springsource.org/browse/SPR-7931

«Проверка аннотации @XmlRootElement должна быть необязательной в Jaxb2Marshaller»

person Matthias M.    schedule 05.10.2012
comment
Пожалуйста, включите в свой ответ больше, чем просто ссылку. Если ссылка когда-либо сломается, пост не поможет будущим пользователям. - person Mick MacCallum; 06.10.2012

Вы можете использовать JaxbElement для классов, у которых нет аннотации @XmlRootElement. Аннотация @XmlRootElement помещается только в объекты верхнего уровня, на которые нет ссылок, если вы генерируете свой код из xsd

Edit

See @Blaise Doughan answer.

@XmlRootElement will be placed only if there is no reference to that type in another type.
person fmucar    schedule 27.01.2011
comment
@XmlRootElement добавляется для всех сгенерированных классов, соответствующих глобальным элементам, независимо от того, есть ли ссылки на эти элементы. - person bdoughan; 27.01.2011