http://blog.csdn.net/yui/article/details/5767494
电信provisioning系统中,常常需要与远程服务器实时交换一些数据,以完成用户的请求。由于简单对象访问协议(Simple Object Access Protocol, SOAP)的流行,许多涉及到第三方的应用,我们一般都比较乐意使用SOAP来开发。不过,由于可能涉及到公司的机密,本系列教程的开发实例尽量采用在网上已经公开的Web Service资源。
前面四节的教程,分别采用了股票信息和天气预报的例子。而这两个实例有一个共同点,SOAP响应消息的数据结构相对简单,只需要按拟定的次序,事先约定返回数据代表的意义,就能够实现无歧义的沟通。这就使得gSOAP能够以字符串数组的形式,定义返回结果,再加上一个整型变量,指示返回结果的个数。
查看一下这两个实例的soapStub.h,可以发现,它们的结果集定义正是这样的:
- struct ns1__ArrayOfString
- {
- int __sizestring; /* sequence of elements <string> */
- char **string; /* optional element of type xsd:string */
- };
但是,如果服务端返回的是一个相对复杂的结果集,事情就不那么好办了。例如,一个提供外汇汇率的Web Service,服务端会同时返回日元、瑞郎、英镑、欧元、澳元、加元、港币合计七种货币兑换美元的汇率情报,每种汇率情报又包括货币代码、当前汇率、涨跌幅、买入价、卖出价、时间戳等多个子项。显然,这不是一个线性结构,而是一个树形结构,就不能使用ArrayOfString来表示了。
这个案例的End point是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx
其WSDL是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx?wsdl
参考前面四节的内容,快速建立其存根程序,不再累述。
我们要实现的API是getExchangeRate,在soapStub.h中搜索,可以发现其返回结果集最终的定义是:
- struct _ns1__getExchangeRateResponse_getExchangeRateResult
- {
- char *xsd__schema; /* required element of type xsd:schema */
- char *__any;
- };
仅仅是两个字符串!于是,最初版本的外汇汇率客户端程序只能这样写:
- #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没有中文字符,而__any含有中文字符,需要转换成GBK编码,具体可以参考前面两节。
编译执行,输出结果如下图:
可以看出,服务端返回的两个长字符串实际上都是基于XML形式的。gSOAP能够自动帮我们完成的也就到此为止,剩下的需要我们自力更生了。
不过,大家也不用头疼,我们还有另外一个利器——libxml2!网上有很多关于libxml2的教程,所以我也不必多说,只要利用其中几个函数和语句即可。
1. xmlParseMemory,字符串转为XML文档
2. xmlDocGetRootElement,获取XML文档根节点
3. xmlStrcmp,比较XML字符串,与strcmp差不多
4. curr = curr->xmlChildrenNode,XML节点指针指向第一个子节点
5. curr = curr->next,XML节点指针指向下一个兄弟节点
6. xmlNodeGetContent,获取XML节点的内容
7. xmlFreeDoc,释放节点,与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;
- }
编译时,需要链接libxml2库,同时指定头文件所在路径:
gcc -O2 -o exchange exchange.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -I/usr/include/libxml2 -L../.. -lgsoap -lxml2
执行结果(部分)如下:
-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