使用libxml进行soap消息封装与解析
来源:互联网 发布:excel数据验证 序列 编辑:程序博客网 时间:2024/05/08 17:46
当使用纯C客户端访问WCF服务时,最麻烦之处在于编写soap消息。如果单纯借助于字符串操作,则既费时又费力,还非常容易出错,因此必须采用更适合的手段。纯C的soap toolkit在sf上找到了csoap(http://sourceforge.net/projects/csoap/?source=directory),试用后发现有很大的局限性,虽然用户面对更少的xml细节,但无法完全按照想要的方式构建soap消息,主要体现在对Envelope/Header无法修改上。后又学习了gSoap(http://sourceforge.net/projects/gsoap2/),这是一个重量级的soap/wsdl框架,适用于从wsdl开始访问web service的方式(https://www.ibm.com/developerworks/cn/webservices/ws-soa-gsoap/),与本文的需求并不一致,遂作罢。重新梳理了一下,其实soap消息无非一个普通的xml格式文本,头部加上几行固定的字符串,核心问题还是xml解析。于是决定用最基本的libxml(http://xmlsoft.org/),w3c官方的工具,专治xml,至于多出的soap部分,copy&paste呗。libxml首先是个纯c的框架,其次解决底层的问题绰绰有余,另外足够简单。所以,应该找不出更好的办法了。
首先,利用libxml按指定格式构建一个xml文件,比如:
该xml文件的树形结构如下图所示:
按照这个结构,从根节点开始,依次生成各子、孙节点,代码如下:
接下来完成soap消息的解析,代码如下:
这里主要是运用xpath对xml文档(WCF服务器发回的soap响应消息)进行解析,得到<echoResult>节点(WCF服务方法调用返回)的内容,并以字符串返回。xpath是w3c标准的xml数据检索语言,相当于sql之于关系数据库。xpath的用法参见:http://xmlsoft.org/examples/index.html#XPath。需要注意的是,示例中节点名称都不带namespace(xmlns),而对于本文的应用,还需要额外增加注册ns的代码,即上述代码段中的14-16行。尤其是16行,注册了一个原xml中没有显式声明的ns:t,用于标识服务方法echo。主函数中调用parseXmlString的部分如下所示:
注意第6行:t:echoResponse,如果不带t,则xpath无法正确解析。
至此,就完成了使用libxml进行soap消息封装与解析。
首先,利用libxml按指定格式构建一个xml文件,比如:
<?xmlversion="1.0" ?><s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <s:Header> <a:Action s:mustUnderstand="1">http://tempuri.org/IMathService/echo</a:Action> <a:To s:mustUnderstand="1">http://127.0.0.1:9998/MathService</a:To> </s:Header> <s:Body> <echo xmlns="http://tempuri.org/"> <input>123</input> </echo> </s:Body></s:Envelope>
该xml文件的树形结构如下图所示:
按照这个结构,从根节点开始,依次生成各子、孙节点,代码如下:
#define BUF_SIZE 4096char* genSoapMsg(char*host, char*port, char*serviceImplementation, char*serviceContract, char*serviceMethod, char*param1, char*param1Val, char*param2, char*param2Val, char*param3, char*param3Val){ // host:port/service char host_service[BUF_SIZE], action[BUF_SIZE]; // 记录树结构各节点的指针 xmlNodePtr nEnvelope, nHeader, nAction, cAction, nMessageID, cMessageID, nReplyTo, nAddress, cAddress, nTo, cTo, nBody, nMethod, nParam1, cParam1, nParam2, cParam2, nParam3, cParam3; // 保存XML文档的指针 xmlDocPtr doc; // XML文档在内存中记录为字符串 xmlChar *xmlbuff; // 用于xmlDocDumpFormatMemory调用, 记录写入XML文档的字符数 int buffersize; // xmlChar字符串转为char字符串,strSoapMsg即为最终生成的soap消息字符串 char *buff,*soapBody; char *soapHead1,*soapHead2,*soapHead3; char soapHead[BUF_SIZE], soapMsg[BUF_SIZE]; sprintf(host_service,"http://%s:%s/%s", host, port, serviceImplementation); sprintf(action,"http://tempuri.org/%s/%s", serviceContract, serviceMethod); // 创建根结点s:Envelope nEnvelope = xmlNewNode(NULL, BAD_CAST"s:Envelope"); xmlNewProp(nEnvelope, BAD_CAST"xmlns:s", BAD_CAST"http://www.w3.org/2003/05/soap-envelope"); xmlNewProp(nEnvelope, BAD_CAST"xmlns:a", BAD_CAST"http://www.w3.org/2005/08/addressing"); // 创建测试文档 doc = xmlNewDoc(BAD_CAST"1.0"); xmlDocSetRootElement(doc, nEnvelope); // 创建子节点s:Header nHeader = xmlNewNode(NULL, BAD_CAST"s:Header"); xmlAddChild(nEnvelope, nHeader); nAction = xmlNewNode(NULL, BAD_CAST"a:Action"); xmlAddChild(nHeader, nAction); xmlNewProp(nAction, BAD_CAST"s:mustUnderstand", BAD_CAST"1"); cAction = xmlNewText(BAD_CAST(action)); xmlAddChild(nAction, cAction); nTo = xmlNewNode(NULL, BAD_CAST"a:To"); xmlAddChild(nHeader, nTo); xmlNewProp(nTo, BAD_CAST"s:mustUnderstand", BAD_CAST"1"); cTo = xmlNewText(BAD_CAST(host_service)); xmlAddChild(nTo, cTo); // 创建子节点s:Body nBody = xmlNewNode(NULL, BAD_CAST"s:Body"); xmlAddChild(nEnvelope, nBody); nMethod = xmlNewNode(NULL, BAD_CAST(serviceMethod)); xmlAddChild(nBody,nMethod); xmlNewProp(nMethod, BAD_CAST"xmlns", BAD_CAST"http://tempuri.org/"); if (param1!= NULL) { nParam1 = xmlNewNode(NULL, BAD_CAST(param1)); xmlAddChild(nMethod, nParam1); cParam1 = xmlNewText(BAD_CAST(param1Val)); xmlAddChild(nParam1, cParam1); } if (param2!= NULL) { nParam2 = xmlNewNode(NULL, BAD_CAST(param2)); xmlAddChild(nMethod, nParam2); cParam2 = xmlNewText(BAD_CAST(param2Val)); xmlAddChild(nParam2, cParam2); } if (param3!= NULL) { nParam3 = xmlNewNode(NULL, BAD_CAST(param3)); xmlAddChild(nMethod, nParam3); cParam3 = xmlNewText(BAD_CAST(param3Val)); xmlAddChild(nParam3, cParam3); } // 保存调试文件 xmlSaveFile(BAD_CAST"SopeRequest.xml", doc); // 把文档内容写入字符串 xmlDocDumpFormatMemory(doc,&xmlbuff, &buffersize, 1); buff = (char*) xmlbuff; soapBody = strchr(buff,'\n')+ 1; // soap消息头 soapHead1 = "POST /MathService HTTP/1.1\r\nContent-Type: application/soap+xml; charset=utf-8\r\nHost: "; soapHead2 = "\r\nContent-Length: "; soapHead3 = "\r\nExpect: 100-continue\r\nConnection: Close\r\n\r\n"; sprintf(soapHead,"%s%s:%s%s%d%s", soapHead1, host, port, soapHead2, strlen(soapBody), soapHead3); sprintf(soapMsg,"%s%s", soapHead, soapBody); xmlFreeDoc(doc); return soapMsg;}
genSoapMsg函数的前五个参数分别是:host服务器地址,port端口号,serviceImplementation服务实现名,serviceContract服务接口名,serviceMethod服务方法名;后六个参数,表示传递给WCF服务方法的参数名称和参数值,也就是目前仅支持最多三个参数。后续这部分仍值得改进。这个函数实现了soap消息的构建,并存储在一个字符串中。调用如下:
soapMessage = genSoapMsg("127.0.0.1","9998", "MathService", "IMathService", "echo", "input", strSend,NULL, NULL, NULL,NULL);printf("发至WCF服务器: %s\n", strSend);
接下来完成soap消息的解析,代码如下:
xmlXPathObjectPtr getNodeSet(xmlDocPtr doc, xmlChar*xpath){ xmlXPathContextPtr context; xmlXPathObjectPtr result; context = xmlXPathNewContext(doc); if (context== NULL) { printf("Error in xmlXPathNewContext\n"); return NULL; } xmlXPathRegisterNs(context, BAD_CAST"s", BAD_CAST"http://www.w3.org/2003/05/soap-envelope"); xmlXPathRegisterNs(context, BAD_CAST"a", BAD_CAST"http://www.w3.org/2005/08/addressing"); xmlXPathRegisterNs(context, BAD_CAST"t", BAD_CAST"http://tempuri.org/"); result = xmlXPathEvalExpression(xpath, context); xmlXPathFreeContext(context); if (result== NULL) { printf("Error in xmlXPathEvalExpression\n"); return NULL; } if(xmlXPathNodeSetIsEmpty(result->nodesetval)) { xmlXPathFreeObject(result); printf("No result\n"); return NULL; } return result; }void parseXmlString(char * xmlString, char * expression, char * keyword, char * result){ xmlDocPtr doc; xmlChar *xpath = BAD_CAST(expression); xmlXPathObjectPtr appResult; xmlNodeSetPtr nodeSet; xmlNodePtr cur; int i; doc = xmlParseMemory(xmlString, strlen(xmlString)+1); // 保存调试文件 xmlSaveFile(BAD_CAST"SoapResponse.xml", doc); appResult = getNodeSet(doc, xpath); if (appResult!= NULL) { nodeSet = appResult->nodesetval; for (i=0; i< nodeSet->nodeNr; i++) { cur = nodeSet->nodeTab[i]; cur = cur->xmlChildrenNode; while (cur !=NULL) { if (!xmlStrcmp(cur->name, BAD_CAST(keyword))) { strcpy(result,((char*)XML_GET_CONTENT(cur->xmlChildrenNode))); goto BREAK; } cur = cur->next; } }BREAK: xmlXPathFreeObject(appResult); xmlCleanupParser(); } xmlFreeDoc(doc);}
这里主要是运用xpath对xml文档(WCF服务器发回的soap响应消息)进行解析,得到<echoResult>节点(WCF服务方法调用返回)的内容,并以字符串返回。xpath是w3c标准的xml数据检索语言,相当于sql之于关系数据库。xpath的用法参见:http://xmlsoft.org/examples/index.html#XPath。需要注意的是,示例中节点名称都不带namespace(xmlns),而对于本文的应用,还需要额外增加注册ns的代码,即上述代码段中的14-16行。尤其是16行,注册了一个原xml中没有显式声明的ns:t,用于标识服务方法echo。主函数中调用parseXmlString的部分如下所示:
while (recv(sHost, buf, sizeof(buf), 0)> 0){ soapResponse = strchr(buf,'<'); ptr = strstr(buf,"</s:Envelope>")+ sizeof("</s:Envelope>")- 1; *ptr ='\0'; parseXmlString(soapResponse,"/s:Envelope/s:Body/t:echoResponse","echoResult", soapParseResult); printf("WCF服务器返回: %s\n", soapParseResult);}
注意第6行:t:echoResponse,如果不带t,则xpath无法正确解析。
至此,就完成了使用libxml进行soap消息封装与解析。
0 0
- 使用libxml进行soap消息封装与解析
- 消息封装与解析
- soap消息的解析
- NSXMLParse 解析 soap消息
- SOAP消息全面解析
- 自定义消息的封装与解析
- 使用gSOAP开发实例(5) libxml2解析SOAP响应消息
- 使用gSOAP开发实例(5) libxml2解析SOAP响应消息
- 使用gSOAP开发实例(5) libxml2解析SOAP响应消息
- 使用SOAP RPC 消息
- 使用soapmonitor模块监视soap请求与响应消息
- 使用EvnetBus进行消息传递解析
- 演练:使用 SOAP 扩展更改 SOAP 消息
- libxml解析
- libxml解析
- libxml中使用xpath解析xml文件
- 【C++】使用libxml解析XML文件
- 【学习笔记】解析XML文档--使用libxml
- opencv安装(WinXp平台下,Qt环境)笔记之一—编译动态链接库和静态链接库文件
- opencv安装(WinXp平台下,Qt环境)笔记之二—测试opencv库安装成功
- IM方案技术调研报告
- Mysql的锁机制解读
- 使用J2EE技术实现其他网站是否正常访问功能5
- 使用libxml进行soap消息封装与解析
- Android EditText样式
- 交通灯管理系统——学习心得
- 嵌入式Linux开发培训资料整理
- 常量、运算符
- vs2012+qt5.2.0环境搭建
- 使用J2EE技术实现其他网站是否正常访问功能
- C语言关键字static
- 关于avro的编译