本篇文章主要讲述项目开发过程中如何使用Xerces-C++,包括xerces的安装,XML的解析,XML的Schemal验证,XML的组装等内容,本文所使用的代码基于xerces-c-3.1.1版本。
一、Xerces简介
Xerces是一个开放源代码的XML语法分析器。Xerces-C++ 的前身是 IBM 的 XML4C 项目。XML4C 和 XML4J 是两个并列的项目,而 XML4J 是 Xerces-J的前身。IBM 将这两个项目的源代码让与 Apache 软件基金会(Apache Software Foundation),他们将其分别改名为 Xerces-C++ 和 Xerces-J。从JDK1.5以后,Xerces就成了JDK的XML默认实现。
Xerces-C++ (通常也称为:Xerces-C)是一套健壮、强大(同时也很庞大)的 XML 解析库,它提供了验证,以及 SAX 和 DOM API。XML 验证在文档类型定义(Document Type Definition,DTD)方面有很好的支持
二、安装xerces
windows下安装稍微麻烦一点,所以在此主要讲解下windows下安装使用xerces的方法,linux下安装的方法比较多,大家可以在网上找找。
Xercesc在windows和linux下都需要先编译后使用。在 UNIX或者类UNIX(比如GUN/Linux、Max OS X、Cygwin、MinGW-MSYS)的环境下编译时,xerces-c均采用GNU automake-base风格的编译过程,这一过程通常分为三步:configure(配置)、make(编译)、make install(安装),这些步骤都需要在msys系统中进行。[1]
我的开发工具选用的是eclipse-cpp,需要安装msys插件,安装后进入msys文件夹(注意msys文件夹在你的开发工具目录下),直接运行msys.bat批处理文件即可运行msys,注意msys是UNIX风格与Windows风格的杂合体,请特别注意前述输入命令中,‘/’的方向与Windows下用于表达路径的‘\’方向正好相反,另外, C字母之后也没有Windows通常用于表示盘符‘:’。
下面详细介绍下安装的过程以及可能出现的问题和解决方法:
1、 configure(配置)
运行msys,进入Xercesc所在目录,在msys中输入以下命令(注意,实际输入时仅一行):
./configure --enable-netaccessor-winsock --enable-transcoder-windows LDFLAGS=-no-undefined --with-PACKAGE
这个配置命令的时间可能需要5分钟左右,可以在msys屏幕中看到一些配置的详细情况,如果在屏幕中没有出现error错误,表示配置成功。
2、 make(编译)
make[回车]
该命令执行时间较长,我机子上大约需要10分钟,需要耐心等待。主要是因为期间不仅编译出xerces-c的二进制库文件,而且把一些例子程序也编译了。
3、 make install(安装)
make install
该命令将把xerces-c的相关例子程序、头文件、二进制库文件,都默认复制到msys目录下的local目录下
4、 整理开发目录
我用的是eclipse-cpp开发环境,需要将msys\ local\ include目录下的xercesc文件夹拷贝到开发工程下的include目录下(这里面存放的是编译后的头文件),然后再将msys\ local\lib目录下的libxerces-c.a和libxerces-c.dll.a两个文件拷贝到开发工程下的lib文件夹中,最后还需将msys\ local\bin目录下的libxerces-c-3-1.dll文件拷贝到工程的根目录下,注意该dll文件必须放在根目录,放在bin目录无效。
这样xercesc的部署工作就完成了,我们就可以使用xercesc进行开发工作了。
三、具体使用
下面将通过一个实际项目中的RQParser和RSGenerator类来讲解XML的具体使用方法,类中使用的解析和组装的方式和流程大家可以当作一个架子在以后的开发中使用。为了让大家方便理解,在类中讲解的方法添加了一些代码和注释,希望对大家有所帮助。
<一>通过RQParser解析类的parse方法和loopXMLElement方法的部分代码具体讲解实际开发中如何使用xercesc进行schemal验证、解析XML。
/**@brief 解析XML字符串,封装到自定义对象中(该方法主要讲解了XML解析的大体流程,有些代码只是为了讲解方便而加上的,如:XML工具的启动与关闭这个一般是放在Tuxedo中启动和关闭的,在此为了方便大家有个整体的理解就加上了)
* @param XML字符串
* @return 一个指向Request对象的智能指针,注:Request对象是项目中自定义对象
*/
boost::shared_ptr<Request> RQParser::parse(const std::string& msg) {
bool m_errorsOccured = false;//程序执行过程中的异常标识
//1、启动xerces-c工具
XMLPlatformUtils::Initialize();
//2、创建XML解析器,并设置相应参数
boost::shared_ptr<XercesDOMParser> m_pParser(new xercesc::XercesDOMParser); //创建XML解析器。注意:此处使用了智能指针,为防止内存泄漏,我后面的代码只要是new的对象都使用了智能指针
XercesDOMParser::ValSchemes gValScheme = XercesDOMParser::Val_Auto;
m_pParser->setValidationScheme(gValScheme);//采用Schemal自动验证
m_pParser->setHandleMultipleImports(true);//可以设置多个schema
m_pParser->setDoNamespaces(true);//设置Parser是否处理xml文档中的名域,如果为true,则Parser增强名域定义的约束和规则。
m_pParser->setDoSchema(true);// 设置Parser处理xml文档中的schema
m_pParser->setValidationSchemaFullChecking(true);//开启完全验证方式
//3、指定用于XML校验的schema
m_pParser->setExternalNoNamespaceSchemaLocation("1E_SpecialServiceReport_RQ.xsd");
m_pParser->cacheGrammarFromParse(true);
m_pParser->useCachedGrammarInParse(true);
m_pParser->loadGrammar("xsd/1E_RQ.xsd",Grammar::SchemaGrammarType,true); //该路径是工程可执行文件的相对路径;该XSD文件只需要指定最终的那一个XSD即可,该XSD引用的其他XSD只需放在与此同一个目录下即可找到;注意:使用之前还需要将包含有需要的XSD文件的文件夹xsd拷贝到与可执行文件相同的路径下。
/*可以指定多个XSD验证
m_pParser->loadGrammar("xsd/1E_RQ2.xsd",Grammar::SchemaGrammarType,true);
*/
//4、指定XSD错误验证处理程序
boost::shared_ptr<XMLErrorReporter> errReporter(new XMLErrorReporter);//创建自定义错误处理类XMLErrorReporter对象,该类继承了xercesc的ErrorHandler类,对错误提示信息进行了实现,注意:此类需要自己实现ErrorHandler的虚函数
m_pParser->setErrorHandler(errReporter.get());//设置Parser的错误处理对象
string msg= < u>xml version=\"1.0\" encoding=\"UTF-8\"?>
";//请求XML字符串
boost::shared_ptr<InputSource> pInputSource(new MemBufInputSource((XMLByte *)msg.c_str(), msg.size() , X("SSRReport_RQ")));//将请求的xml字符串读取到内存中
//5、解析XML
try{
m_pParser->parse(*pInputSource);//Parser解析器调用parse方法解析XML,安装解析器设置的各种参数,执行Schemal验证,将XML读取到内存,并成为一个DOM树形结构
}catch{
m_errorsOccured = true;
return 1;
}
if(m_pParser->getErrorCount()!=0){ //解析过程中有错误
//错误处理逻辑
}
//6、遍历XML,取得所需数据
if (!m_errorsOccured) {
xercesc::DOMDocument * m_doc = m_pParser->getDocument();//取得DOM数据
if (m_doc) {
DOMNodeList *nodelist = m_doc->getElementsByTagName(XMLString::transcode ("RQ"));//定位并获取RQ元素,该元素是请求XML的根元素
XMLSize_t nSize=0;//XMLSize_t是xercesc中定义的一个类型,xercesc很多参数和返回值都使用了这个类型
if(nodelist!=NULL){
nSize = nodelist->getLength();
}
for (XMLSize_t i = 0; i < nSize; ++i) {
DOMNode * node = nodelist->item(i);//取得该位置对应的node节点
loopXMLElement(node);//调用一个自定义的方法,循环遍历该节点下的所有元素
}
XMLString::release(&nodelist);//释放资源,注意:由XMLString::transcode使用的资源必须释放
}
}
//7、销毁XML解析工具
XMLPlatformUtils::Terminate();
}
/**@brief 遍历DOM节点,采用递归算法,遍历该节点下的所有元素极其属性值(该方法主要是用于上面的parse方法的调用)
* @param 需要遍历的节点
* @return 0:表示遍历成功,1:表示失败
*/
int RQParser::loopXMLElement(DOMNode *node) {
DOMNode * child;
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
char * nodeName = XMLString::transcode(node->getNodeName());//获取该节点的名称
std::string nodeName_str = nodeName;
//如果该节点下有属性,则遍历其属性
if (node->hasAttributes()) {
DOMNamedNodeMap *pAttributes = node->getAttributes();
const XMLSize_t nSize = pAttributes->getLength();
//遍历该节点下的所有属性
for (XMLSize_t i = 0; i < nSize; ++i) {
DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
char *name = XMLString::transcode(pAttributeNode->getName());//获取属性名
std::string name_str = name;
//获取SpecialServiceReport_RQ下的UserID属性的值
if (name_str == "UserID" &&
nodeName_str== "SpecialServiceReport_RQ") {
char *attrValue = XMLString::transcode(
pAttributeNode->getValue());//获取属性值
……//将属性值存放到返回的对象中,此处自己构造对象填值
XMLString::release(&attrValue);//释放资源
}
……//获取SpecialServiceReport_RQ下其他属性及其子节点的属性的逻辑
XMLString::release(&name);
}
}
XMLString::release(&nodeName);
}
//递归每个子节点
if (node->hasChildNodes()) {
for (child = node->getFirstChild(); child != 0; child = child->getNextSibling()) {
loopXMLElement(child);
}
}
return 0;
}
<二>通过RSGenerator组装类的generateXML的部分代码来具体讲解实际开发中如何使用xercesc组装XML
/**@brief 根据自定义对象,组装XML字符串(该方法是组装XML的一个模型,以后的开发可以照着套)
* @param req:一个指向Request对象的指针;
* @param msg:组装后返回的XML字符串
* @return 操作结果:0:正确,1:错误
*/
int RSGenerator::generateXML(boost::shared_ptr<Request> req,std::string& msg){
//1、启动xerces-c工具
XMLPlatformUtils::Initialize();
//2、组装XML
xercesc::DOMImplementation=xercesc::DOMImplementationRegistry::getDOMImplementation(XMLString::transcode("Core"));
if (m_pimpl != NULL) {
try {
//在内存中创建DOM
m_pDOMDoc = m_pimpl->createDocument(0,
XMLString::transcode ("RS"), // 根节点名称
0);
DOMElement* rootElem =m_pDOMDoc->getDocumentElement();//获取根节点
//设置根节点属性
rootElem->setAttribute(XMLString::transcode ("xmlns:xsi"), XMLString::transcode ("http://www.w3.org/2001/XMLSchema-instance"));
……//设置其他属性
//将DOM转换成字符串
DOMLSOutput * p_DOMLSoutput = m_pimpl->createLSOutput();
DOMLSSerializer * theSerializer = m_pimpl->createLSSerializer();
XMLCh* encoding = XMLString::transcode("UTF-8");
p_DOMLSoutput->setEncoding(encoding);//设置字符编码
p_DOMLSoutput->setByteStream(new MemBufFormatTarget());
theSerializer->write(m_pDOMDoc, p_DOMLSoutput);
XMLFormatTarget * p_XMLFormatTarget = p_DOMLSoutput->getByteStream();
//将转换后的字符串存到msg返回
msg = (char*) ((xercesc::MemBufFormatTarget*)p_XMLFormatTarget)->getRawBuffer();
XMLString::release(&encoding);
//释放资源
p_DOMLSoutput->release();
theSerializer->release();
m_pDOMDoc->release();
} catch (exception e) {
return 1;
}
}else {