Почему проверка на корневой элемент требуется в Jaxb2Marshaller? - PullRequest
5 голосов
/ 26 января 2011

Я использую 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;
}

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

Ответы [ 3 ]

5 голосов
/ 26 января 2011

Почему проверяется аннотация @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 для предоставления имени / пространства имен корневого элемента.

2 голосов
/ 05 октября 2012

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

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

1 голос
/ 27 января 2011

Вы можете использовать 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.
...