Spring Web Service中JAXB反序列化问题:SOAP请求参数对应JAXBElement为空
嘿,我之前也踩过Spring Web Service里JAXB反序列化的坑,看你说WSDL生成完全符合预期,但SOAP请求里的参数对应JAXBElement全是空的,这事儿确实头疼,给你几个实用的排查方向和解决办法:
1. 先检查端点方法的参数注解和定义
你代码里用了@PayloadRoot,但重点要注意:你的WSDL里是三个独立的message part,这种情况下端点方法的参数定义很容易出错。
首先,如果你是直接把每个part作为单独的JAXBElement参数,得确保每个参数的类型和注解完全匹配WSDL的定义:
- 每个
JAXBElement的泛型类型要对应WSDL里的part类型(比如JAXBElement<RequestorIDType>对应RequestorID part) - 更关键的是,WSDL里每个part都有自己的命名空间:RequestorID用的是
cla前缀的命名空间,RequestSub和RequestStatus是req前缀的,你生成的JAXB类里,对应的@XmlType或者@XmlElement的namespace属性必须和这些URI完全一致,差一个字符都不行!
另外,端点方法的参数要正确用@RequestPayload标注,比如:
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "test") @ResponsePayload public JAXBElement<YourResponseType> handleTestRequest( @RequestPayload JAXBElement<RequestorIDType> requestorID, @RequestPayload JAXBElement<RequestSubType> requestSub, @RequestPayload JAXBElement<RequestStatusType> requestStatus) { // 这里处理业务逻辑 }
如果觉得多个JAXBElement参数太麻烦,更稳妥的办法是生成一个包裹类,把这三个字段都包含进去,让JAXB把整个SOAP Body映射到这个类上。比如生成一个ActiveInput类,里面有RequestorIDType requestorID、RequestSubType requestSub、RequestStatusType requestStatus三个字段,每个字段加上对应的@XmlElement注解(指定名称和命名空间),然后端点方法直接用@RequestPayload ActiveInput input作为参数,这样反而不容易出错。
2. 核对SOAP请求的XML结构是否完全匹配WSDL
用SOAP UI发请求的时候,很多人容易忽略命名空间和标签名称的细节,这也是导致JAXB识别不到参数的重灾区。你的请求Body必须严格对应WSDL的定义:
- 每个参数标签的命名空间要和WSDL里的
cla、req前缀对应的URI完全一致 - 标签名称要和WSDL里的part名称大小写完全一致(比如WSDL里是
RequestorID,请求里就不能写成requestorId或者RequestorId) - 最外层的
test标签的命名空间必须和你@PayloadRoot里的NAMESPACE_URI一致
举个正确的请求示例:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cla="http://your-cla-namespace-uri-here" xmlns:req="http://your-req-namespace-uri-here"> <soapenv:Header/> <soapenv:Body> <test xmlns="http://your-test-namespace-uri-here"> <cla:RequestorID>你的请求ID值</cla:RequestorID> <req:RequestSub>你的请求子项值</req:RequestSub> <req:RequestStatus>你的请求状态值</req:RequestStatus> </test> </soapenv:Body> </soapenv:Envelope>
你可以把SOAP UI里的请求和WSDL的message定义仔细对比,哪怕是一个字母的大小写或者命名空间的小错误,都会导致JAXB反序列化失败,返回空的JAXBElement。
3. 检查JAXB生成的实体类是否正确
如果你是用xjc工具生成的JAXB类,一定要检查生成的类的注解是否和WSDL匹配:
- 打开
RequestorIDType.java,看类上的@XmlType(namespace = "...")是不是WSDL里cla对应的URI - 检查每个字段的
@XmlElement(name = "...")是不是和WSDL里的part名称完全一致 - 要是有
@XmlRootElement注解,也要确认它的name和namespace属性是否正确
有时候xjc生成类的时候,会因为绑定文件(bindings.xjb)的配置问题,自动修改了字段名称或者命名空间,这时候就得手动调整注解,或者修改绑定文件重新生成。
4. 开启Spring WS日志,看原始SOAP请求
如果上面的排查都没找到问题,那建议你开启Spring WS的消息日志,这样能看到服务器实际接收到的完整SOAP请求内容,方便你对比WSDL找差异。只需要在配置文件里加几行:
# application.properties里的配置 logging.level.org.springframework.ws.server.MessageTracing.sent = TRACE logging.level.org.springframework.ws.server.MessageTracing.received = TRACE
启动服务后,发个SOAP请求,就能在日志里看到完整的请求XML,包括所有的命名空间和标签,这时候哪里不匹配一眼就能看出来。
我当初就是因为SOAP请求里的一个命名空间少写了个后缀,折腾了大半天,你按照上面的步骤一步步排查,应该能很快解决问题!
备注:内容来源于stack exchange,提问作者pppp

