2016-11-08 96 views
1

我工作在一個環境中,所有常用的依賴jar分別位於tomcat/lib文件夾和war文件中的應用程序特定jar文件中。無法編組類型,彈簧控制器的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下,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響應。

回答

1

我已經重現了一個小的Spring啓動例子的錯誤,所以我很確定它不是類路徑問題。

問題是,當正在構建Hateoas Resources類的JAXBContext時,沒有對您的AccountResource類的引用。這意味着當Spring要求JAXB序列化你的ResponseEntity時,它在遇到AccountResource時會中斷,因爲這個類沒有在用於序列化的JAXBContext中註冊。

如果你在你的控制器中直接返回ResponseEntity的方法,你可以看到這工作正常。

JAXBContext是不可變的,並且據我所見,無法影響JAXBContext的構造,因爲AbstractJaxb2HttpMessageConverter.getJaxbContext()是最終的。

我不是JAXB方面的專家,但從文檔看來,Resource.getContent()使用@XmlAnyElement正確註釋,但由於某些原因AccountResource未在資源內序列化。

如果我的分析是正確的,那麼每個使用Hateoas和XML的人都會遇到問題,所以要麼沒有人這樣做,要麼我錯了。你真的需要它來生成XML嗎?

如果我必須進一步調試,我會首先檢查Hateoas源代碼,看看它們是否有任何測試來驗證XML序列化是否真正起作用,如果沒有測試,就有可能完全破壞。

編輯 如果你可以沒有命名空間*我相信我找到了解決方案。

如果我MappingJackson2XmlHttpMessageConverter替換默認Jaxb2RootElementHttpMessageConverter,並使用@JacksonXmlRootElement,我可以得到下面的輸出(*它可能會增加使用混入的命名空間,但我沒有檢查)。

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

爲了修改施工後需要春季4.1 HttpMessageConverters。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)); 
}