2016-11-08 3 views
1

Я работаю в среде, где все общие зависимые баночки находятся в папке tomcat/lib и конкретных приложениях в военном файле.Невозможно маршалировать тип, выход XML для контроллера весны

У меня есть простой контроллер и с помощью пружинного-hateoas

@RestController 
@ExposesResourceFor(AccountResource.class) 
@RequestMapping("/accounts") 
public class AccountController { 

    @RequestMapping(method = { RequestMethod.GET }) 
    public ResponseEntity<Resources<AccountResource>> getAccounts() { 
     List<Account> accounts = //get list of accounts; 
     return new ResponseEntity<Resources<AccountResource>>(
      this.accountResourceAssembler.toEmbeddedList(accounts), 
      HttpStatus.OK); 
    } 
} 


@XmlRootElement(name = "account") 
@Relation(value = "account", collectionRelation = "accounts") 
public class AccountResource extends ResourceWithEmbeddeds { 
    private Account account; 

    //getters 
} 

как весенний hateoas банка в TOMCAT/Lib, XML-сортировочный из Resources класса не работает, выбрасывает ошибку, как указано в конце.

Можно ли установить дочерний загрузчик классов в преобразователь Jaxb в конфигурации пружины, чтобы избежать этой ошибки?

com.sun.istack.internal.SAXException2: unable to marshal type "package.AccountResource" as an element because it is not known to this context. 
    com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:234) 
    com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:323) 
    com.sun.xml.internal.bind.v2.runtime.property.ArrayReferenceNodeProperty.serializeListBody(ArrayReferenceNodeProperty.java:103) 
    com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:144) 
    com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:345) 
    com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:578) 
    com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:326) 
    com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:479) 
    com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308) 
    com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236) 
    org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter.writeToResult(Jaxb2RootElementHttpMessageConverter.java:187) 
    org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter.writeInternal(AbstractXmlHttpMessageConverter.java:66) 
    org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:195) 
    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:239) 
    org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183) 
    org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) 
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) 
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) 
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) 
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) 
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) 
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:687) 

Невозможно переместить банки, поэтому необходимо исправить положение пружины. BTW, ответ JSON работает очень хорошо, проблема заключается только в ответе XML для списка.

ответ

1

Я воспроизвел ошибку с небольшим примером загрузки Spring, поэтому я уверен, что это не проблема класса.

Проблема заключается в том, что при создании класса JAXBContext для класса Hateoas Resources ссылка на ваш класс AccountResource отсутствует. Это означает, что, когда Spring запрашивает JAXB для сериализации вашего ResponseEntity, он ломается, когда встречается с AccountResource, потому что этот класс не зарегистрирован в JAXBContext, который используется для сериализации.

Если вы создаете в своем контроллере метод, который возвращает ResponseEntity напрямую, вы можете видеть, что это работает нормально.

JAXBContext неизменен, и насколько я вижу, нет никакого способа повлиять на конструкцию JAXBContext, поскольку AbstractJaxb2HttpMessageConverter.getJaxbContext() является окончательным.

Я не эксперт в JAXB, но из документации выглядит, что Resource.getContent() правильно аннотируется с помощью @XmlAnyElement, но почему-то AccountResource не сериализуется внутри Resource.

Если мой анализ верен, это будет проблемой для всех, кто использует Hateoas с XML, так что либо никто этого не делает, либо я ошибаюсь. Вам действительно нужно это для создания XML?

Если бы мне пришлось отлаживать это, я бы начал с проверки источника Hateoas и посмотрел, есть ли у них какой-либо тест, который проверяет, что сериализация XML действительно работает, если нет теста, есть шанс, что он полностью сломан ,

Редактировать Если вы можете жить без пространств имен * Я считаю, что нашел решение.

Если я заменяю Jaxb2RootElementHttpMessageConverter по умолчанию с MappingJackson2XmlHttpMessageConverter и использовать @JacksonXmlRootElement, я могу получить следующий результат (* Может быть, можно добавить пространство имен с помощью Примеси, но я не проверял).

<Resources xmlns=""> 
<links></links> 
<content> 
    <content> 
     <account> 
      .... 
     </account> 
     <links></links> 
    </content> 
</content> 

Для того, чтобы модифицировать HttpMessageConverters после строительства нужно Spring 4.1.3 или более поздней версии, и вы можете расширить конфигурацию WebMvcConfigurationSupport, это позволит вам сделать следующее:

@Override 
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { 
    for (Iterator<HttpMessageConverter<?>> iterator = converters.iterator(); iterator.hasNext();) { 
     HttpMessageConverter<?> converter = iterator.next(); 
     if (converter instanceof Jaxb2RootElementHttpMessageConverter) { 
      iterator.remove(); 
     } 
    } 

    ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.xml().applicationContext(this.getApplicationContext()).build(); 
    converters.add(new MappingJackson2XmlHttpMessageConverter(objectMapper)); 
} 
Смежные вопросы