gpt4 book ai didi

java - 无法编码类型,Spring Controller 的 XML 输出

转载 作者:行者123 更新时间:2023-11-30 07:58:24 26 4
gpt4 key购买 nike

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

我有一个简单的 Controller 并使用 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 hateoas 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)

我无法移动 jar ,所以需要修复 Spring 侧。顺便说一句,JSON 响应工作正常,问题仅在于列表的 XML 响应。

最佳答案

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

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

如果您在 Controller 中创建一个直接返回 ResponseEntity 的方法,您会发现它工作正常。

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

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

如果我的分析是正确的,那么这对于使用 Hateoas 和 XML 的每个人来说都是一个问题,所以要么没有人这样做,要么我错了。您真的需要它来生成 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));
}

关于java - 无法编码类型,Spring Controller 的 XML 输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40481369/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com