電信provisioning系統(tǒng)中,常常需要與遠(yuǎn)程服務(wù)器實(shí)時(shí)交換一些數(shù)據(jù),以完成用戶的請(qǐng)求。由于簡(jiǎn)單對(duì)象訪問(wèn)協(xié)議(Simple Object Access Protocol, SOAP)的流行,許多涉及到第三方的應(yīng)用,我們一般都比較樂(lè)意使用SOAP來(lái)開(kāi)發(fā)。不過(guò),由于可能涉及到公司的機(jī)密,本系列教程的開(kāi)發(fā)實(shí)例盡量采用在網(wǎng)上已經(jīng)公開(kāi)的Web Service資源。
前面四節(jié)的教程,分別采用了股票信息和天氣預(yù)報(bào)的例子。而這兩個(gè)實(shí)例有一個(gè)共同點(diǎn),SOAP響應(yīng)消息的數(shù)據(jù)結(jié)構(gòu)相對(duì)簡(jiǎn)單,只需要按擬定的次序,事先約定返回?cái)?shù)據(jù)代表的意義,就能夠?qū)崿F(xiàn)無(wú)歧義的溝通。這就使得gSOAP能夠以字符串?dāng)?shù)組的形式,定義返回結(jié)果,再加上一個(gè)整型變量,指示返回結(jié)果的個(gè)數(shù)。
查看一下這兩個(gè)實(shí)例的soapStub.h,可以發(fā)現(xiàn),它們的結(jié)果集定義正是這樣的:
struct ns1__ArrayOfString
{
int __sizestring; /* sequence of elements <string> */
char **string; /* optional element of type xsd:string */
};
但是,如果服務(wù)端返回的是一個(gè)相對(duì)復(fù)雜的結(jié)果集,事情就不那么好辦了。例如,一個(gè)提供外匯匯率的Web Service,服務(wù)端會(huì)同時(shí)返回日元、瑞郎、英鎊、歐元、澳元、加元、港幣合計(jì)七種貨幣兌換美元的匯率情報(bào),每種匯率情報(bào)又包括貨幣代碼、當(dāng)前匯率、漲跌幅、買入價(jià)、賣出價(jià)、時(shí)間戳等多個(gè)子項(xiàng)。顯然,這不是一個(gè)線性結(jié)構(gòu),而是一個(gè)樹(shù)形結(jié)構(gòu),就不能使用ArrayOfString來(lái)表示了。
這個(gè)案例的End point是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx
其WSDL是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx?wsdl
參考前面四節(jié)的內(nèi)容,快速建立其存根程序,不再累述。
我們要實(shí)現(xiàn)的API是getExchangeRate,在soapStub.h中搜索,可以發(fā)現(xiàn)其返回結(jié)果集最終的定義是:
struct _ns1__getExchangeRateResponse_getExchangeRateResult
{
char *xsd__schema; /* required element of type xsd:schema */
char *__any;
};
僅僅是兩個(gè)字符串!于是,最初版本的外匯匯率客戶端程序只能這樣寫:
#include <iconv.h>
#include "soapH.h"
#include "ExchangeRateWebServiceSoap12.nsmap"
int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
iconv_t conv = iconv_open(dest, src);
if ( conv == (iconv_t) -1 )
return -1;
memset(output, 0, olen);
if ( iconv(conv, &input, &ilen, &output, &olen) )
return -1;
iconv_close(conv);
return 0;
}
int main(int argc, char **argv) {
if ( argc != 2 && argc != 3 ) {
printf("Usage: %s type [end_point]\n", argv[0]);
printf("\ttype = A : all rate\n");
printf("\ttype = B : basic rate\n");
printf("\ttype = C : cross rate\n");
exit(-1);
}
struct soap soap;
soap_init(&soap);
// don't set is OK
//soap_set_mode(&soap, SOAP_C_UTFSTRING);
struct _ns1__getExchangeRate request;
struct _ns1__getExchangeRateResponse response;
request.theType = argv[1];
char *endpoint = NULL;
if ( argc == 3 )
endpoint = argv[2];
if ( soap_call___ns3__getExchangeRate(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
printf("%s\n", response.getExchangeRateResult->xsd__schema);
printf("----------\n");
int ilen = strlen(response.getExchangeRateResult->__any);
int olen = ilen * 2;
char *output = (char *) malloc(sizeof(char) * olen);
conv_charset("GBK", "UTF-8", response.getExchangeRateResult->__any, ilen, output, olen);
printf("%s\n", output);
free(output);
}
else {
soap_print_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
其中,xsd__schema沒(méi)有中文字符,而__any含有中文字符,需要轉(zhuǎn)換成GBK編碼,具體可以參考前面兩節(jié)。
編譯執(zhí)行,輸出結(jié)果如下圖:

可以看出,服務(wù)端返回的兩個(gè)長(zhǎng)字符串實(shí)際上都是基于XML形式的。gSOAP能夠自動(dòng)幫我們完成的也就到此為止,剩下的需要我們自力更生了。
不過(guò),大家也不用頭疼,我們還有另外一個(gè)利器——libxml2!網(wǎng)上有很多關(guān)于libxml2的教程,所以我也不必多說(shuō),只要利用其中幾個(gè)函數(shù)和語(yǔ)句即可。
1. xmlParseMemory,字符串轉(zhuǎn)為XML文檔
2. xmlDocGetRootElement,獲取XML文檔根節(jié)點(diǎn)
3. xmlStrcmp,比較XML字符串,與strcmp差不多
4. curr = curr->xmlChildrenNode,XML節(jié)點(diǎn)指針指向第一個(gè)子節(jié)點(diǎn)
5. curr = curr->next,XML節(jié)點(diǎn)指針指向下一個(gè)兄弟節(jié)點(diǎn)
6. xmlNodeGetContent,獲取XML節(jié)點(diǎn)的內(nèi)容
7. xmlFreeDoc,釋放節(jié)點(diǎn),與free差不多
最終的外匯匯率客戶端程序如下:
#include <iconv.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include "soapH.h"
#include "ExchangeRateWebServiceSoap12.nsmap"
#define FIELD_LEN 16
int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
iconv_t conv = iconv_open(dest, src);
if ( conv == (iconv_t) -1 )
return -1;
memset(output, 0, olen);
if ( iconv(conv, &input, &ilen, &output, &olen) )
return -1;
iconv_close(conv);
return 0;
}
int main(int argc, char **argv) {
if ( argc != 2 && argc != 3 ) {
printf("Usage: %s type [end_point]\n", argv[0]);
printf("\ttype = A : all rate\n");
printf("\ttype = B : basic rate\n");
printf("\ttype = C : cross rate\n");
exit(-1);
}
struct soap soap;
soap_init(&soap);
// don't set is OK
//soap_set_mode(&soap, SOAP_C_UTFSTRING);
struct _ns1__getExchangeRate request;
struct _ns1__getExchangeRateResponse response;
request.theType = argv[1];
char *endpoint = NULL;
if ( argc == 3 )
endpoint = argv[2];
if ( soap_call___ns3__getExchangeRate(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
int len = strlen(response.getExchangeRateResult->__any);
xmlDocPtr pdoc = xmlParseMemory(response.getExchangeRateResult->__any, len);
xmlNodePtr root = xmlDocGetRootElement(pdoc);
xmlNodePtr curr = root;
while ( xmlStrcmp(curr->name, (const xmlChar *) "getExchangeRate") )
curr = curr->xmlChildrenNode;
for ( curr = curr->xmlChildrenNode; curr; curr = curr->next ) {
xmlNodePtr data;
for ( data = curr->xmlChildrenNode; data; data = data->next ) {
char ifield[FIELD_LEN];
char ofield[FIELD_LEN];
strcpy(ifield, xmlNodeGetContent(data));
if ( conv_charset("GBK", "UTF-8", ifield, strlen(ifield), ofield, FIELD_LEN) )
printf("%s\t%s\n", data->name, ifield);
else
printf("%s\t%s\n", data->name, ofield);
}
printf("\n");
}
xmlFreeDoc(pdoc);
}
else {
soap_print_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
編譯時(shí),需要鏈接libxml2庫(kù),同時(shí)指定頭文件所在路徑:
gcc -O2 -o exchange exchange.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -I/usr/include/libxml2 -L../.. -lgsoap -lxml2
執(zhí)行結(jié)果(部分)如下:
-bash-3.2$ ./exchange B
Code JPY
Currency 日元
ClosePrice 87.08
DiffPercent -0.29%
DiffAmount -0.25
OpenPrice 87.5
HighPrice 87.71
LowPrice 87.04
Range 0.77%
BuyPrice 87.08
SellPrice 87.12
ChangeColor Green
DataTime 16:57:54
Code
CHF
Currency 瑞郎
ClosePrice 1.0552
DiffPercent 0.16%
DiffAmount 0.0017
OpenPrice 1.054
HighPrice 1.0552
LowPrice 1.0498
Range 0.51%
BuyPrice 1.0552
SellPrice 1.0556
ChangeColor Red
DataTime 16:57:52
http://blog.csdn.net/yui/archive/2010/07/26/5767494.aspx