【问题标题】:Unable to marshal type, XML output for spring controller无法编组类型,弹簧控制器的 XML 输出
【发布时间】:2017-03-21 18:03:10
【问题描述】:

我在一个环境中工作,所有常见的依赖 jar 都位于 tomcat/lib 文件夹中,而特定于应用程序的 jar 位于 war 文件中。

我有一个简单的控制器并使用 spring-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
}

由于 spring hatoas jar 在 tomcat/lib 中,Resources 类的 XML 编组不起作用,抛出最后提到的错误。

是否可以在spring配置中将子类加载器设置为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)

我无法移动罐子,因此需要在弹簧侧进行修复。顺便说一句,JSON 响应工作得很好,问题只是列表的 XML 响应。

【问题讨论】:

    标签: java spring spring-mvc jaxb spring-hateoas


    【解决方案1】:

    我已经用一个小的 Spring boot 示例重现了该错误,所以我很确定这不是类路径问题。

    问题在于,在构建 Hateoas Resources 类的 JAXBContext 时,没有对您的 AccountResource 类的引用。这意味着当 Spring 要求 JAXB 序列化您的 ResponseEntity 时,它会在遇到 AccountResource 时中断,因为此类未在用于序列化的 JAXBContext 中注册。

    如果您在控制器中创建一个直接返回 ResponseEntity 的方法,您可以看到它工作正常。

    JAXBContext 是不可变的,据我所知,没有办法影响 JAXBContext 的构造,因为 AbstractJaxb2HttpMessageConverter.getJaxbContext() 是最终的。

    我不是 JAXB 方面的专家,但从文档看来,Resource.getContent() 似乎正确地用 @XmlAnyElement 注释,但由于某种原因,AccountResource 没有在 Resource 中序列化。

    如果我的分析是正确的,这对于使用带有 XML 的 Hateoas 的每个人来说都是一个问题,所以要么没有人这样做,要么我错了。你真的需要它来生成 XML 吗?

    如果我必须进一步调试,我会先检查 Hateoas 源代码,看看他们是否有任何测试可以验证 XML 序列化是否确实有效,如果没有测试,则它有可能完全损坏。

    编辑 如果您可以在没有名称空间的情况下生活*,我相信我已经找到了解决方案。

    如果我用 MappingJackson2XmlHttpMessageConverter 替换默认的 Jaxb2RootElementHttpMessageConverter,并使用@JacksonXmlRootElement,我可以得到以下输出(*可以使用 MixIns 添加命名空间,但我没有检查)。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-13
      • 1970-01-01
      • 2021-09-18
      • 1970-01-01
      • 2014-10-28
      • 2016-04-04
      • 1970-01-01
      相关资源
      最近更新 更多