1. C++解析XML的开源库
在项目中XML的解析使用的是开源的第三方库,TinyXML;这个解析库的模型通过XML文件,然后再内存中生成DOM模型,从而让我们可以很方便的遍历这颗XML树。
DOM模型即文档对象模型,是将整个文档分成多个元素(如:书、章、节、段等),并利用树型结构表示这些元素之间的顺序关系以及嵌套包含关系。先看一下TinyXML中的主要类和XML文档之间的对应关系,下图是TinyXML中主要class的类图,反应各个类之间的静态关系。
TiXmlBase是所有类的基类,TiXmlNode、TiXmlAttribute两个类都继承自TiXMLBase类,其中TiXmlNode类指的是所有被<...>...<.../>包括的内容,而xml中的节点又具体分为以下几方面内容,分别是声明、注释、节点以及节点间的文本,因此在TiXmlNode基础上又衍生出来这几个类TiXmlComment、TiXmlDeclaration、TiXmlDocument、TiXmlElement、TiXmlText、TiXmlUnknown,分别用来指明具体是xml中的哪一部分。TiXmlAttribute类不同于TiXmlNode,它指的是在尖括号里面的内容,像<...***=...>,其中***就是一个属性,这里采用一个XML文档具体说明一下:
1.<?xml version="1.0" encoding="UTF-8"?> 2.<phonebook> 3. <!--one item behalfs one contacted person.--> 4. <item> 5. <name>miaomaio</name> 6. <addr>Shaanxi Xi'an</addr> 7. <tel>13759911917</tel> 8. <email>[email protected]</email> 9. </item> 10. <item> 11. <name>gougou</name> 12. <addr>Liaoning Shenyang</addr> 13. <tel>15840330481</tel> 14. <email>[email protected]</email> 15. </item> 16. <!--more contacted persons.--> </phonebook>
l 像TiXmlDeclaration指的就是<?xml version="1.0" encoding="UTF-8"?>,
l 像TiXmlComment指的就是<!--one item behalfs one contacted person.-->、<!--more contacted persons.-->,
l 像TiXmlDocument指的就是整个xml文档,
l 像TiXmlElement指的就是<phonebook>、<item>、<name>、<addr>等等这些节点,
l 像TiXmlText指的就是‘gougou’、‘15840330481’这些夹在<item>与</item>、<name>与</name>、<addr>与</addr>之间的文本文字,
l 像TiXmlAttribute指的就是<?xml version="1.0" encoding="UTF-8"?>节点中version、encoding,
l 除此之外就是TiXmlUnknown。
1) 读取XML文件
//!xml文件读取,模拟调用接口返回char *字符串 filebuf *pbuf; ifstream filestr; long size; char *buffer; filestr.open(XML_EXAMPLE_FILE_NAME, ios::binary); pbuf = filestr.rdbuf(); size = pbuf->pubseekoff(0,ios::end,ios::in); pbuf->pubseekpos(0, ios::in); buffer = new char[size]; pbuf->sgetn(buffer, size); filestr.close(); //!从xml字符串中获取相关值 string strCreationTime; string strJobId; string strJobType; string strJobName; string strJobLeader; TiXmlDocument *xmlDocument = new TiXmlDocument(); xmlDocument->Parse(buffer, 0, TIXML_DEFAULT_ENCODING); TiXmlElement *RootElement = xmlDocument->RootElement(); TiXmlElement *fileHeaderElement = RootElement->FirstChildElement(); TiXmlElement *fileBodyElement = fileHeaderElement->NextSiblingElement(); for (TiXmlElement *nodeElement = fileHeaderElement->FirstChildElement(); nodeElement; nodeElement = nodeElement->NextSiblingElement()) { string strElementKey = nodeElement->Value(); if (strElementKey.compare(XML_PARSE_CREATION_TIME) == 0) { strCreationTime = nodeElement->GetText(); } } for (TiXmlElement *nodeElement = fileBodyElement->FirstChildElement(); nodeElement; nodeElement = nodeElement->NextSiblingElement()) { string strElementKey = nodeElement->Value(); if (strElementKey.compare(XML_PARSE_JOB_ID) == 0) { strJobId = nodeElement->GetText(); } if (strElementKey.compare(XML_PARSE_JOB_TYPE) == 0) { strJobType = nodeElement->GetText(); } if (strElementKey.compare(XML_PARSE_JOB_NAME) == 0) { strJobName = nodeElement->GetText(); } if (strElementKey.compare(XML_PARSE_JOB_LEADER) == 0) { strJobLeader = nodeElement->GetText(); } }
2) 创建、生成XML文件
以如下XML文件为例:
<?xml version=”1.0” encoding=”gb2312”> <InterFaceFile> <FileHeader> <MessageType>ProxyMiddleWareJobSearch</MessageType> <Originator>WetLand</Originator> <Recipient>Platform</Recipient> <CreationTime>2014-9-18 10:25:20</CreationTime> </FileHeader> <FileBody> <UserName>...</UserName> <UserRoleID>13</UserRoleID> <JobType></JobType> </FileBody> </InterFaceFile>
//! 构建XML字符串 TiXmlDocument *pDoc = new TiXmlDocument; TiXmlDeclaration *pDeclaration = new TiXmlDeclaration("1.0", "gb2312", ""); pDoc->LinkEndChild(pDeclaration); TiXmlElement *pEleRoot = new TiXmlElement("InterFaceFile"); pDoc->LinkEndChild(pEleRoot); TiXmlElement *pEleFileHeader = new TiXmlElement("FileHeader"); TiXmlElement *pEleFileBody = new TiXmlElement("FileBody"); pEleRoot->LinkEndChild(pEleFileHeader); pEleRoot->LinkEndChild(pEleFileBody); TiXmlElement *pEleMessageType = new TiXmlElement("MessageType"); TiXmlElement *pEleOriginator = new TiXmlElement("Originator"); TiXmlElement *pEleRecipient = new TiXmlElement("Recipient"); TiXmlElement *pEleCreationTime = new TiXmlElement("CreationTime"); pEleFileHeader->LinkEndChild(pEleMessageType); pEleFileHeader->LinkEndChild(pEleOriginator); pEleFileHeader->LinkEndChild(pEleRecipient); pEleFileHeader->LinkEndChild(pEleCreationTime); TiXmlText *pEleMessageTypeText = new TiXmlText("ProxyMiddleWareJobSearch"); TiXmlText *pEleOriginatorText = new TiXmlText("WetLand"); TiXmlText *pEleRecipientText = new TiXmlText("Platform"); TiXmlText *pEleCreationTimeText = new TiXmlText("2014-9-18 10:25:20"); pEleMessageType->LinkEndChild(pEleMessageTypeText); pEleOriginator->LinkEndChild(pEleOriginatorText); pEleRecipient->LinkEndChild(pEleRecipientText); pEleCreationTime->LinkEndChild(pEleCreationTimeText); //! TiXmlElement *pEleUserName = new TiXmlElement("UserName"); TiXmlElement *pEleUserRoleID = new TiXmlElement("UserRoleID"); TiXmlElement *pEleJobType = new TiXmlElement("JobType"); pEleFileBody->LinkEndChild(pEleUserName); pEleFileBody->LinkEndChild(pEleUserRoleID); pEleFileBody->LinkEndChild(pEleJobType); TiXmlText *pEleUserNameText = new TiXmlText("..."); TiXmlText *pEleUserRoleIDText = new TiXmlText("13"); TiXmlText *pEleJobTypeText = new TiXmlText(""); pEleUserName->LinkEndChild(pEleUserNameText); pEleUserRoleID->LinkEndChild(pEleUserRoleIDText); pEleJobType->LinkEndChild(pEleJobTypeText); pDoc->SaveFile("E:/houqd.xml");
3) 生成XML字符串
生成XML字符串只需要将如上的pDoc->SaveFile(“E:/houqd.xml”)替换为如下:
//!生成string字符串 TiXmlPrinter printer; pDoc->Accept(&printer); string strRequest(printer.CStr());
2. C++调SOAP的开源库
我们采用gsoap作为开源的web service的实现框架,可以从网上下载到开源的代码实现。下载地址:http://gsoap2.sourceforge.net/ 一般下载的gsoap工具包中已经包含了生成Web Service客户端需要用到的两个工具(可执行文件):wsdl2h.exe和soapcpp2.exe,windows开发包一般在gsoap/bin/win32下,默认情况下wsdl2h并不支持SSL,即无法访问HTTPS站点,如果想要支持SSL等更多功能,就需要自己重新配置编译该gsoap工具包,以生成新的wsdl2h.exe和soapcpp2.h。这里,我采用的是原生的文件。
相关参考资料:http://blog.csdn.net/zhaiwenjuan/article/details/6590941
1) gsoap生成本地代理
1> 根据wsdl生成相应的头文件
从Web服务提供者处获取Web Service的WSDL文件,通常是一个URL,如:http://www.somewebservice.com/Service?Wsdl,当然也可以是一个WSDL形式的XML文件。
使用gsoap的wsdl2h.exe,根据WSDL生成一个C/C++语法结构的头文件。
例如:wsdl2h.exe -s -o Service.h http://www.somewebservice.com/Service?Wsdl
这一步执行完后会得到一个头文件,如:Service.h
该步的目的:实现WSDL文件到.h文件的数据映射。
2> 根据生成的头文件生成相关代理文件
使用gsoap的预编译器soapcpp2.exe,根据上一步得到的头文件来生成存根文件soapStub.h和客户端代码框架:
如:soapcpp2.h -i -x -C -L Service.h
这一步将会得到几个.nsmap、.h和.cpp文件。该步的目的:生成相应的底层通信代码。
2) gsoap调用webservice接口
将上几步生成的文件加入到工程中,并包含响应的头文件,并采用如下的调用方式:
string strRequest(printer.CStr()); //!web service调接口 string strWebServiceAddr = "http://172.16.10.209:8080/HDHT_J2EE/services/ProxyMiddleWareJob?wsdl"; ProxyMiddleWareJobHttpBindingProxy *proxy = new ProxyMiddleWareJobHttpBindingProxy(); _ns1__ProxyMiddleWareJobSearch inputParam; _ns1__ProxyMiddleWareJobSearchResponse outputParam; inputParam.in0 = const_cast<char *>(strRequest.c_str()); proxy->ProxyMiddleWareJobSearch(NULL,strWebServiceAddr.c_str() , &inputParam, outputParam); string strResult = outputParam.out;
3. 联调过程中出现的问题
1) 参数传递问题
这里,在使用web service调用接口时,根据以前的思路(PHP的web service中的调用方式),想当然的将C++里面调用时参数的传递当成直接传递了,例如:上面调用ProxyMiddleWareJobSearch接口时,它接收一个xml格式的字符串,并返回一个xml格式的字符串,因此在刚开始使用时,采取的调用方式为:
Proxy->ProxyMiddleWareJobSearch(NULL, strWebServiceAddr.c_str(), strRequest, strResult);采用的字符串形式,结果一直报错,意思是无法将const char *转换为_ns1__ProxyMiddleWareJobSearch *的形式,在后来运用中发现应该如下使用:
_ns1__ProxyMiddleWareJobSearch inputParam; //! 代表输入
_ns1__ProxyMiddleWareJobSearchResponse outputParam; //!代表输出
inputParam.in0 = const_cast<char *>(strRequest.c_str()); //!输入第一个参数,
如果接口需要第二个参数,则调用方式为:inputParam.in1 = ...
string strResult = outputParam.out; //! 调用后结果的返回。
2) 中文乱码问题
解决方法:在Java构建web service端,返回字符串之前,先进行base64的加密,然后c++作为web service的client调用完方法后,先对字符串进行base64的解密,这样就不存在乱码问题了。
C++实现Base64的代码参考链接:
http://www.cnblogs.com/phinecos/archive/2008/10/10/1308272.html