最近遇到一个 Axis 调用 CXF 发布的 WebService 时报错的问题,在此记录一下问题解决的过程。
客户使用 Axis 调用我方使用 CXF 发布的 WebService 报错。报错信息为意外的元素,如下图所示
从报错信息看,WebService 端需要的参考类型是 <{}arg0>
,而接收到的参数是 (uri:"http://tempuri.org/", local:"xmlParameter")
。经过搜索,发现需要在参数上添加注解,显式注明 namespace 和参数名:@WebParam(name = "xmlParameter", targetNamespace = "http://tempuri.org/")
。修改完成后,本地测试依然报错,如下图所示
有了之前的经验,很快发现需要在方法上添加注解注明 action
:@WebMethod(action = "http://tempuri.org/getAllPurchasePlane")
,同时,也针对返回值也添加了注解,注明返回值的 namespace
:@WebResult(targetNamespace = "http://tempuri.org/")
。本地使用 Axis 测试调用成功,故更新至测试系统与客户联调,发现还是报错,但报错信息变为类型转换错误,如下图所示。
根据错误信息查找多次后仍未解决,此时,只好询问客户是否可以提供调用代码。获取调用代码后,发现确实调用代码有所不同,如下图所示。
使用客户提供的调用代码测试确实调用不成功,但 WebService 确实是按照客户提供的文档发布的,同时发现 WebService 端返回值类型为 String[]
,但调用方接收到的是 List
类型,所以导致了转换出错。由此怀疑是不是客户调用方法有问题,但沟通后反馈说其他系统对接过,没有问题,此时问题陷入了死胡同。
面对这种问题,需要看透问题的本质,WebService 的调用依靠的是 WSDL 描述调用属性,既然我们调用不成功,会不会是生成的 WSDL 有问题呢?按照这个思路,咨询客户获得了一份之前对接成功的系统的 WSDL。经过比对,果然发现返回值类型并不是文档中写明的 String[]
,而是 ArrayOfString
,如下图所示:
经过搜索,很快就找到了 ArrayOfString
的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>
* Java class for ArrayOfString complex type.
* <p>
* The following schema fragment specifies the expected content contained within
* this class.
*
* <pre>
* <complexType name="ArrayOfString">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="string" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ArrayOfString", propOrder = { "string" })
public class ArrayOfString {
@XmlElement(nillable = true)
protected List<String> string;
/**
* Gets the value of the string property.
* <p>
* This accessor method returns a reference to the live list, not a snapshot.
* Therefore any modification you make to the returned list will be present
* inside the JAXB object. This is why there is not a <CODE>set</CODE> method
* for the string property.
* <p>
* For example, to add a new item, do as follows:
*
* <pre>
* getString().add(newItem);
* </pre>
* <p>
* Objects of the following type(s) are allowed in the list {@link String }
*/
public List<String> getString() {
if (string == null) {
string = new ArrayList<String>();
}
return this.string;
}
}
|
最终 WebService 代码修改如下:
1
2
3
4
5
6
7
8
|
@WebMethod(action = "http://tempuri.org/getAllPurchasePlane")
public @WebResult(targetNamespace = "http://tempuri.org/") ArrayOfString getAllPurchasePlane(
@WebParam(name = "xmlParameter", targetNamespace = "http://tempuri.org/") String xmlParameter) {
ArrayOfString ret = new ArrayOfString();
ret.getString().add("-1");
ret.getString().add("xml格式不正确");
return ret;
}
|
修改代码后,本地测试通过,再和客户联调也验证通过,问题至此解决。
但疑问还没有消除,根据 ArrayofString
的源码,其实也只是封装了一个 List<String>
,为什么使用 Axis 调用后的返回值可以转换成 String[]
?希望了解的人告知一二。