接收到的XML的XSLT转换之后,JAXB域对象的Spring MVC@RequestBody



情况是供应商为他们的XML文档提供了一个XML模式,他们可以提交给我的服务。我不喜欢他们的模式,所以我编写了自己的模式和XSLT来转换接收到的XML。我的模式与JAXB的xjc工具一起用于生成.java文件,这些文件将一些pojo绑定到合适的对象模型中。如果不是因为需要一个转换步骤,那么在SpringMVC中实现这一点将是微不足道的。

接收到的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"搜索处理的是转换输出而不是输入。

我怀疑是否有这样的开箱即用的东西,但你应该能够沿着以下几条线构建一些可重复使用的东西:

为自己定义一个新的注释,比如@XmlWithTransform,它接受xslt位置的参数,您可以在控制器上这样指定:

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

现在编写一个自定义HandlerMethodArgumentResolver,它可以接收请求体xml,使用annotation的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>

最新更新