2012-09-24 24 views
1

情況是,供應商爲其XML文檔提供了XML架構,供他們提交給我的服務。我不喜歡它們的模式,所以我編寫了自己的模式和XSLT來轉換接收到的XML。我的模式與JAXB的xjc工具一起使用來生成將某些pojos綁定到合適的對象模型中的.java文件。如果不是因爲需要進行轉換步驟,那麼在Spring MVC中實現這將會很簡單。JAXB Domain對象的Spring MVC @RequestBody AFTER收到的XML的XSLT轉換

收到的XML在映射到JAXB類之前必須首先進行轉換。大致類似於下面的代碼片段:

@RequestMapping(value="/receiveXml", method=RequestMethod.POST) 
public ResponseEntity<String> receiveXml(@RequestBody String vendorXmlPayload)   {  
    // 1. Make sure vendorXmlPayload adheres to vendor's schema 
    vendorSchema.newValidator().validate(new StreamSource(new StringReader(vendorXmlPayload))); 

    // 2. Transform xml payload to my schema 
    StringWriter sw = new StringWriter(); 
    transformer.transform(new StreamSource(new StringReader(vendorXmlPayload)), new StreamResult(sw)) 

    // 3. Validate transformed XML against my schema 
    mySchema.newValidator().validate(new StreamSource(new StringReader(sw.toString()))); 

    // 4. Unmarshall to JAXB-annotated classes 
    DomainObject obj = (DomainObject) unmarshaller.unmarshal(new StreamSource(new StringReader(sw.toString()))); 


    (errors != null) ? return ... HttpStatus.BAD_REQUEST : return ..... HttpStatus.OK 
}  

是否有一些優雅Spring註解凝聚所有這一切對MVC控制器?也就是說有沒有辦法通過@RequestBody註解來執行變換&解組?也許就像這個虛構的代碼片段:

@RequestMapping(value="/receiveXml", method=RequestMethod.POST) 
@Transform(transformer="myTransform.xslt") 
public ResponseEntity<String> receiveXml(@RequestBody DomainObj domainObj)   
{ 
    // Process my DomainObj as I normally would 
    (errors != null) ? return ... HttpStatus.BAD_REQUEST : return ..... HttpStatus.OK 
} 

@InitBinder看起來不太適合這種情況。大多數「Spring MVC XSLT」搜索處理轉換輸出而不是輸入。

回答

2

我懷疑是否有這樣的事開箱即用,但你應該能夠建立可重複使用的東西沿着這些線路:

定義爲自己新的註釋說@XmlWithTransform,它接受的參數說了XSLT的位置,你可以在控制器上指定是這樣的:

@RequestMapping(value="/receiveXml", method=RequestMethod.POST) 
    public ResponseEntity<String> receiveXml(@XmlWithTransform(usingxslt="anxsl.xsl")  CustomType customType) 

現在編寫自定義HandlerMethodArgumentResolver可以採取在請求主體XML,改造它使用註解的XSLT參數,並將其綁定到類型指定爲參數,沿着這些線:

import java.io.StringReader; 

import javax.servlet.http.HttpServletRequest; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.transform.stream.StreamSource; 

import org.springframework.core.MethodParameter; 
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; 
import org.springframework.http.server.ServletServerHttpRequest; 
import org.springframework.web.bind.support.WebDataBinderFactory; 
import org.springframework.web.context.request.NativeWebRequest; 
import org.springframework.web.method.support.HandlerMethodArgumentResolver; 
import org.springframework.web.method.support.ModelAndViewContainer; 

public class XsltTransformingHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver{ 

    Unmarshaller unmarshaller; 
    @Override 
    public boolean supportsParameter(MethodParameter parameter){ 
     return (parameter.getMethodAnnotation(XmlWithTransform.class)!=null); 
    } 

    @Override 
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
     XmlWithTransform xmlWithTransform = parameter.getMethodAnnotation(XmlWithTransform.class); 
     Class<?> parameterType = parameter.getParameterType(); 
     String xsltLocation = xmlWithTransform.usingxstl(); 
     ServletServerHttpRequest servletRequest = new ServletServerHttpRequest(webRequest.getNativeRequest(HttpServletRequest.class)); 
     String xmlFromVendor = IOUtils.toString(servletRequest.getBody(), "UTF-8"); 

     String xmlInternal = transform(xmlFromVendor, xsltLocation); 
     return unmarshaller.unmarshal(new StreamSource(new StringReader(xmlInternal))); 
    } 
} 

並註冊這個說法解析器使用Spring MVC:

<mvc:annotation-driven> 
    <mvc:argument-resolvers> 
     <bean class="XsltTransformingHandlerMethodArgumentResolver"></bean> 
    </mvc:argument-resolvers> 
</mvc:annotation-driven>