如何让CXF忽略命名空间?解决服务响应无命名空间的解组异常
解决CXF调用服务时的Unmarshal命名空间不匹配问题
问题描述
使用WSDL生成的CXF类调用服务时,抛出如下反序列化错误:
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.some.com/vxs", local:"Extended"). Expected elements are <{http://www.some.com/vxs}Material>,<{http://www.some.com/vxs},<{http://www.some.com/vxs}Duration>...
问题根源:WSDL中声明了命名空间http://www.some.com/vxs,但服务实际返回的响应XML里,元素没有带该命名空间,导致JAXB反序列化时不匹配。
已尝试的无效方案
- 参考过相关反序列化错误解决方案,但未解决问题
- 尝试关闭schema验证,代码如下,仍报错:
((BindingProvider) conversionService.getConversionServicePort()).getRequestContext().put("schema-validation-enabled", "false");
当前代码配置
服务客户端构建代码
public static ConversionService buildConversionService() throws MalformedURLException { URL wsdlUrl = new URL("https://my-site.com/ConversionService?wsdl"); ConversionService_Service conversionService_service = new ConversionService_Service(wsdlUrl); return conversionService_service.getConversionServicePort(); }
CXF代码生成插件配置
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>3.0.2</version> <executions> <execution> <id>generate-sources4</id> <phase>generate-sources</phase> <goals> <goal>wsdl2java</goal> </goals> <configuration> <sourceRoot>${basedir}/target/generated</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${project.basedir}/src/wsdl/ConversionService.wsdl</wsdl> <extraargs> <extraarg>-client</extraarg> </extraargs> </wsdlOption> </wsdlOptions> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> <version>2.8.1</version> </dependency> </dependencies> </plugin>
可行解决方案
方案1:修改响应XML的拦截器
通过CXF的入站拦截器,在JAXB反序列化前修改响应XML,给无命名空间的元素添加上WSDL中声明的命名空间。示例代码:
public static ConversionService buildConversionService() throws MalformedURLException { URL wsdlUrl = new URL("https://my-site.com/ConversionService?wsdl"); ConversionService_Service conversionService_service = new ConversionService_Service(wsdlUrl); ConversionService port = conversionService_service.getConversionServicePort(); // 添加自定义入站拦截器处理命名空间 Client client = ClientProxy.getClient(port); client.getInInterceptors().add(new AbstractPhaseInterceptor<Message>(Phase.RECEIVE) { @Override public void handleMessage(Message message) throws Fault { try { // 读取响应流并转为字符串 InputStream inputStream = message.getContent(InputStream.class); String xmlContent = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); // 替换无命名空间的元素为带目标命名空间的格式 String targetNs = "http://www.some.com/vxs"; xmlContent = xmlContent.replaceAll("<([^/>]+)>", "<ns1:$1 xmlns:ns1=\"" + targetNs + "\">") .replaceAll("</([^>]+)>", "</ns1:$1>"); // 将修改后的字符串重新转为输入流放回消息中 message.setContent(InputStream.class, new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8))); } catch (IOException e) { throw new Fault(e); } } }); return port; }
注:如果JDK版本低于11,需要用Apache Commons IO的IOUtils.toString方法替代readAllBytes。
方案2:通过JAXB绑定文件自定义生成规则
创建JAXB绑定文件(如src/main/resources/bindings.xml),指定生成的实体类忽略命名空间:
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" jaxb:version="2.1"> <jaxb:bindings schemaLocation="http://www.some.com/vxs" node="/xsd:schema"> <jaxb:globalBindings namespace=""/> </jaxb:bindings> </jaxb:bindings>
然后修改CXF插件配置,引入该绑定文件:
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>3.0.2</version> <executions> <execution> <id>generate-sources4</id> <phase>generate-sources</phase> <goals> <goal>wsdl2java</goal> </goals> <configuration> <sourceRoot>${basedir}/target/generated</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${project.basedir}/src/wsdl/ConversionService.wsdl</wsdl> <extraargs> <extraarg>-client</extraarg> <extraarg>-b</extraarg> <extraarg>${project.basedir}/src/main/resources/bindings.xml</extraarg> </extraargs> </wsdlOption> </wsdlOptions> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> <version>2.8.1</version> </dependency> </dependencies> </plugin>
重新生成代码后,JAXB实体类的@XmlElement注解会去掉命名空间属性,匹配无命名空间的响应。
方案3:手动修改生成的JAXB实体类
找到生成的对应实体类(如Extended.java、Material.java),将类中@XmlElement注解的namespace属性删除或设为空字符串:
// 修改前 @XmlElement(name = "Extended", namespace = "http://www.some.com/vxs") // 修改后 @XmlElement(name = "Extended")
缺点是每次重新生成代码都会覆盖修改,适合临时测试场景。
为什么之前的方案无效
关闭schema-validation-enabled只是关闭了XML Schema的结构合法性验证,但JAXB反序列化的核心是检查元素的命名空间是否与实体类注解中的命名空间匹配,所以该配置无法解决命名空间不匹配的问题。
内容的提问来源于stack exchange,提问作者akvyalkov
相关产品推荐
相关产品推荐

