Spring SOAP客户端中Serializable类型的反序列化报错问题
解决SOAP服务反序列化Serializable类型元素的问题
问题背景
在Spring Boot项目中使用cxf-codegen-plugin的wsdl2java工具生成SOAP服务存根时,接收响应出现反序列化错误。核心问题是WSDL中ValidationResult类型的value字段被定义为ax27:Serializable,但服务实际返回的是包含具体字段的ASNKey对象,导致解析时抛出异常。
相关代码片段
WSDL关键片段
<xs:schema xmlns:ax27="http://io.java/xsd" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://validation.common.services.scope.xxx.com/xsd"> <xs:import namespace="http://io.java/xsd"/> <xs:complexType name="ValidationResult"> <xs:sequence> <xs:element maxOccurs="unbounded" minOccurs="0" name="all" nillable="true" type="ax25:ValidationMessage"/> <xs:element minOccurs="0" name="value" nillable="true" type="ax27:Serializable"/> </xs:sequence> </xs:complexType> </xs:schema> <xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://io.java/xsd"> <xs:complexType name="Serializable"> <xs:sequence/> </xs:complexType> </xs:schema>
pom.xml插件配置
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>${cxf.version}</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${basedir}/src/main/resources/MyService.wsdl</wsdl> <wsdlLocation>classpath:MyService.wsdl</wsdlLocation> <bindingFiles> <bindingFile>${basedir}/src/main/resources/binding/bindings.xml</bindingFile> </bindingFiles> <packagenames> <packagename>http://io.java/xsd=com.xxx.io.java.xsd</packagename> <packagename>http://rmi.java/xsd=com.xxx.rmi.java.xsd</packagename> </packagenames> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin>
绑定文件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:version="2.0"> <jaxb:globalBindings generateElementProperty="false" generateIsSetMethod="true" optionalProperty="primitive"> <xjc:serializable uid="12343"/> </jaxb:globalBindings> </jaxb:bindings>
服务响应示例
<ax25:all type="com.xxx.scope.services.common.validation.ValidationMessage"> <ax25:code>6100161</ax25:code> <ax25:error>true</ax25:error> <ax25:info>false</ax25:info> <ax25:message>Quantity is required</ax25:message> <ax25:objectId/> <ax25:objectType/> <ax25:type>error</ax25:type> <ax25:warning>false</ax25:warning> </ax25:all> <ax25:value type="com.xxx.scope.cbo.data.asn.ASNKey"> <ax21:aaaNumber>PR2406030009</ax21:aaaNumber> <ax21:businessUnit>88</ax21:businessUnit> </ax25:value>
报错信息
Caused by: com.sun.istack.SAXParseException2: unexpected element (uri:"http://aaa.data.cbo.scope.xxx.com/xsd", local:"asnNumber"). Expected elements are (none)
原因分析
- WSDL中定义的
Serializable是一个空的复杂类型,wsdl2java生成的Java类中没有任何字段,无法匹配响应中实际返回的ASNKey结构。 - 服务端通过类似Java序列化的方式传递对象,但WSDL未正确定义实际类型结构,仅标记为Serializable,导致客户端存根无法解析具体内容。
解决方案
方案1:修改WSDL替换Serializable为实际类型
如果有权限修改WSDL,直接将value字段的类型替换为实际的ASNKey类型,并导入对应的Schema:
<!-- 修改后的ValidationResult --> <xs:complexType name="ValidationResult"> <xs:sequence> <xs:element maxOccurs="unbounded" minOccurs="0" name="all" nillable="true" type="ax25:ValidationMessage"/> <!-- 替换为ASNKey类型 --> <xs:element minOccurs="0" name="value" nillable="true" type="ax21:ASNKey"/> </xs:sequence> </xs:complexType> <!-- 导入ASNKey对应的命名空间Schema --> <xs:import namespace="http://aaa.data.cbo.scope.xxx.com/xsd"/>
重新生成存根后,ValidationResult类会包含ASNKey类型的value字段,可正常解析响应。
方案2:通过绑定文件映射Serializable到实际类
无法修改WSDL时,通过JAXB绑定文件将ax27:Serializable映射到项目中的ASNKey类:
- 确保项目中已存在
ASNKey类(可手动创建或通过对应Schema生成) - 修改绑定文件
bindings.xml,添加类型映射:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:version="2.0"> <jaxb:globalBindings generateElementProperty="false" generateIsSetMethod="true" optionalProperty="primitive"> <xjc:serializable uid="12343"/> </jaxb:globalBindings> <!-- 将Serializable类型映射到ASNKey类 --> <jaxb:bindings schemaLocation="http://io.java/xsd" node="/xs:schema/xs:complexType[@name='Serializable']"> <jaxb:class name="ASNKey" implClass="com.xxx.scope.cbo.data.asn.ASNKey"/> </jaxb:bindings> </jaxb:bindings>
- 执行
mvn generate-sources重新生成存根,此时value字段会被映射为ASNKey类型。
方案3:自定义反序列化适配器
若上述方法均不可行,自定义JAXB适配器处理Serializable类型的解析:
- 创建适配器类:
public class SerializableAdapter extends XmlAdapter<Object, Serializable> { @Override public Serializable unmarshal(Object v) throws Exception { if (v instanceof Element) { Element element = (Element) v; ASNKey asnKey = new ASNKey(); asnKey.setAaaNumber(element.getElementsByTagNameNS("http://aaa.data.cbo.scope.xxx.com/xsd", "aaaNumber").item(0).getTextContent()); asnKey.setBusinessUnit(Integer.parseInt(element.getElementsByTagNameNS("http://aaa.data.cbo.scope.xxx.com/xsd", "businessUnit").item(0).getTextContent())); return asnKey; } return null; } @Override public Object marshal(Serializable v) throws Exception { // 若无需发送该字段,返回null即可 return null; } }
- 在绑定文件中配置适配器:
<jaxb:bindings schemaLocation="http://validation.common.services.scope.xxx.com/xsd" node="/xs:schema/xs:complexType[@name='ValidationResult']/xs:sequence/xs:element[@name='value']"> <jaxb:property> <jaxb:typeAdapters> <jaxb:typeAdapter value="com.xxx.adapter.SerializableAdapter"/> </jaxb:typeAdapters> </jaxb:property> </jaxb:bindings>
验证
重新编译项目,调用SOAP服务,检查是否能正常接收响应,无反序列化错误抛出。
内容的提问来源于stack exchange,提问作者VeePee
相关产品推荐
相关产品推荐

