使用 java axis 呼叫 .net web service 全記錄

由於手邊的某個案子需要用 java 去 call .net 的 web service,
上網 google 了半天之後得到一個 workable 的做法 ...

種類 1 - String HelloWorld(String param1) : 

String wsSoapAction = "http://tempuri.org/";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(wsUrl);
call.setUseSOAPAction(true);
call.setOperationName(new QName(wsSoapAction, "HelloWorld")); // 設置要調用哪個方法
// 設置要傳遞的參數
call.addParameter(
new QName(wsSoapAction, "param1"),
org.apache.axis.encoding.XMLType.XSD_STRING,
javax.xml.rpc.ParameterMode.IN);
call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING); // 標準字串
call.setUseSOAPAction(true);
call.setSOAPActionURI(wsSoapAction + "HelloWorld");
String sRtn = (String) call.invoke(new Object[] { "this is a param" }); // 調用方法並傳遞參數

種類 2 - DataSet HelloWorld(String param1) : 這回傳的居然是 .net vb 直接 select database 的 dataSet, 它是以 xml 格式呈現, 同上面只是把 returnType 改成 :

...
call.setReturnType(org.apache.axis.encoding.XMLType.XSD_SCHEMA);  // DataSet (XML)
...
Schema schema = (Schema) call.invoke(new Object[] { "test_id" }); // 調用方法並傳遞參數

回傳的東東用這樣去處理 :
MessageElement[] me = schema.get_any();
List head = me[0].getChildren(); // header/schema
List body = me[1].getChildren(); // data
if (body != null && body.size() > 0) {
NodeList node = me[1].getChildNodes().item(0).getChildNodes();
for (int i=0; i NodeList node1 = node.item(i).getChildNodes();
for (int j=0; jnode1.getLength(); j++) {

Node n = node1.item(j);
String name = n.getNodeName();
String value = n.getFirstChild() == null?"":n.getFirstChild().getNodeValue();
log.debug("name="+name+",value="+value);
}
}
}

這樣取值的原因是 .net 的 dataSet xml 長這樣 ... (第一個 element 是 schema, 第二個 element 是 data)



------------------------------------------------------------------------------
(以上是第一次開發的記錄)

過了兩年之後, 發現了另外一種方法, 可以大幅簡化開發, 就是直接使用 wsdl gen 出 java class :

step 1, 先取得 .net web service 的 wsdl, 在 service 的測試頁


點第一行的 "服務描述", 就可以得到 wsdl, 另存新檔 xxxx.wsdl, 丟進 eclipse 的專案中


step 2, 在 eclipse 裡 gen 出 web service client :


這樣 gen 出對應的 class 之後, 之前相同的 method 只需要這樣寫 :
(那 package name 取決於原始 web service 設定的 namespace, org.tempuri 都是偷懶沒改的)

org.tempuri.Service service = new org.tempuri.ServiceLocator(qsURL);
org.tempuri.ServiceSoap ss = service.getServiceSoap();
String rtn = ss.HelloWorld("this is a param");

簡潔了許多

----------------------------------------------------------------------------
最新的一個難題是 : input 參數中有一個 type 是 DataSet !!

一樣取得 wsdl gen 出 web service client 之後, 不知道要怎麻組出, .net 認得的 dataSet xml ...
只好拿另一個回傳的 dataSet, show 出 xml, 一個一個比對, 把它兜出來 ...
中間還試過, 直接拿 xml 字串 parse 成 MessageElement, 吐出來的 xml 明明就很像, 可是 service 就是無法執行, like this ...

Document XMLDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xmlString)));
Element element = XMLDoc.getDocumentElement();
_any[0] = new MessageElement(element);

拼拼湊湊了半天, 以下的 code 終於 work, .net web service 可以接下 java 傳過去的 DataSet :

org.tempuri.TestService service = new org.tempuri.TestServiceLocator(qsURL);
org.tempuri.TestServiceSoap ss = service.getTestServiceSoap();

// 取回 dataSet 用來比對我們要產生的 dataSet
TestServiceGetDetailResponseTestServiceGetDetailResult x = ss.TestServiceGetDetail(oSchoolId);

TestSendSDataSet sDataSet = new TestSendSDataSet();
MessageElement[] _any = new MessageElement[2];

// schema declare
_any[0] = new MessageElement("schema", "xs", "http://www.w3.org/2001/XMLSchema");
_any[0].addNamespaceDeclaration("msdata", "urn:schemas-microsoft-com:xml-msdata");
SOAPElement se = _any[0].addChildElement("element");
se.setAttribute("name", "NewDataSet");
se.setAttribute("msdata:IsDataSet", "true");
se.setAttribute("msdata:UseCurrentLocale", "true");
se = se.addChildElement("complexType").addChildElement("choice").addChildElement("element");
se.setAttribute("name", "Table");
se = se.addChildElement("complexType").addChildElement("sequence");
se.addChildElement("element").setAttribute("name", "school_id");
se.addChildElement("element").setAttribute("name", "school_user");

// data
_any[1] = new MessageElement("diffgram", "diffgr", "urn:schemas-microsoft-com:xml-diffgram-v1");
_any[1].addNamespaceDeclaration("msdata", "urn:schemas-microsoft-com:xml-msdata");
se = _any[1].addChildElement("NewDataSet", "", "").addChildElement("Table");
se.addChildElement("school_id").addTextNode("123456");
se.addChildElement("school_user").addTextNode("林木森");
sDataSet.set_any( _any );
System.out.println("xml10="+x.get_any()[0].toString());
System.out.println("xml11="+sDataSet.get_any()[0].toString());
System.out.println("xml20="+x.get_any()[1].toString());
System.out.println("xml21="+sDataSet.get_any()[1].toString());

sRtn = ss.TestSend(sDataSet);

----------------------------------------------------------------------------------------------

下回應該也把我們家對外的 service 介面加個這標準的東東才對,
現在的介面實在太低級了