前一阵子学习Xerces-C++用于解析指定格式XML文档。在这里,把自己的学习经历和大家分享一下,在这里仅仅讲一些入门的知识,希望对大家有所帮助。
Xerces-C++是什么?
Xerces-C++ 的前身是 IBM 的 XML4C 项目。XML4C 和 XML4J 是两个并列的项目,而 XML4J 是 Xerces-J——Java 实现——的前身。IBM 将这两个项目的源代码让与 Apache 软件基金会(Apache Software Foundation),他们将其分别改名为 Xerces-C++ 和 Xerces-J。这两个项目是 Apache XML 组的核心项目(如果看到的是“Xerces-C”而不是“Xerces-C++”,也是同一个东西,因为这个项目一开始就是用 C(译者注:原文为C++)语言编写的)。
Xerces-C++: 功能介绍
Xerces-C++是一个非常健壮的 XML 解析器,其提供的两种解析XML文档的方法,DOM和SAX (我是采用DOM方法)。
SAX是一个面向事件的编程API.一个解析引擎消耗XML序列数据,并在发现进来的XML数据的结构时回调应用程序,这些回调称为事件句柄.
DOM与SAX不同,它允许对XML文档进行编辑并保存为一个文件或者流,还允许以编程的方式构建一个XML文档.DOM提供了一个内存中的模型,你可以遍历文档树,删除节点或者嫁接新节点.与解析的SAX事件不同,DOM事件反映出用户与文档的互动以及使用文档的改变.
总的来说,SAX是按行遍历XML文档的,而DOM是先把XML文档生成树,然后遍历DOM树,来解析每个节点.
Xerces-C++:学习的过程
1、平台选择:
在学习Xerces-C++之前你必须选择一种应用平台,可以是windows、linux、cygwin,以及solaris等系统平台。在这里,我选用的是Redhat Enterprise Linux AS3,选用的Xerces-C++ 是xerces-c-src_2_7_0.tar.gz,可以从官方网站:http://www.apache.org/ 直接下载。
2、编译源码
由于我下载下来的是源码,所以需要对其进行编译,否则我们无法加载库文件。
首先进入你的工作目录:cd /home/olcom/laubo(这是我当前工作目录)
然后解压你的源码包: tar zxvf xerces-c-src_2_7_0.tar.gz
设置包含源代码的环境变量:
export XERCESCROOT=/home/olcom/laubo/xerces-c-src_2_7_0
进入目录:cd xerces-c-src_2_7_0/src/xercesc
运行脚本生成makefile文件:
./runConfigure -plinux -cgcc -xg++ -C--prefix=/opt/ApacheXML
选项: -p 为操作系统平台
-c C 编译器
-x C++编译器
-c 库的配置路径
编译源码:make
make install
(编译可能要花费你好一会儿,在我的机器上花费大约7分钟的时间,所以要耐心等候)
3、学习类库
因为类库很大,所以刚开始,我并没有选择去分析与阅读类库,我是先在网上了一个比较完整的例子,然后对其进行编译和调试,然后从例子下手去分析类库所提供的接口。这里,我把自己的程序简化了一下,希望可以作为大家学习的例子。
首先,我们需要定义一种 XML文档的样式。在这里,我们简单的定义一种样式(含有中文),如下:
<?xml version= "1.0 " encoding= "utf-8 " standalone= "no "?> <国家调查> <Node1> <subNode> <subNode1> <subNode11> china 111-> 江苏 </subNode11> <subNode11> china 112-> 天津 </subNode11> <subNode11> china 113-> 北京 </subNode11> <subNode11> china 114-> 上海 </subNode11> <subNode11> china 115-> 广州 </subNode11> </subNode1> </subNode> <subNode1> Asia 12-> 韩国 </subNode1> <subNode2> Asia 13-> 日本 </subNode2> <subNode3> Asia 14-> 越南 </subNode3> <subNode4> Asia 15-> 柬埔寨 </subNode4> <subNode5> Asia 16-> 老挝 </subNode5> </Node1> <Node2> <subNode> America 21-> 巴西 </subNode> <subNode> America 22-> 阿根廷 </subNode> <subNode> America 23-> 智利 </subNode> <subNode> America 24-> 墨西哥 </subNode> <subNode> America 25-> 巴拉圭 </subNode> <subNode> America 26-> 美国 </subNode> <subNode> America 27-> 加拿大 </subNode> </Node2> <Node3> <subNode> Europe 31-> 英国 </subNode> <subNode> Europe 32-> 意大利 </subNode> <subNode> Europe 33-> 法国 </subNode> <subNode> Europe 34-> 德国 </subNode> <subNode> Europe 35-> 西班牙 </subNode> <subNode> Europe 36-> 匈牙利 </subNode> </Node3> <Node5> THE END </Node5> </国家调查>
//CXML.h #ifndef XML_PARSER_HPP #define XML_PARSER_HPP #include <xercesc/util/TransService.hpp> #include <xercesc/dom/DOM.hpp> #include <xercesc/dom/DOMDocument.hpp> #include <xercesc/dom/DOMDocumentType.hpp> #include <xercesc/dom/DOMElement.hpp> #include <xercesc/dom/DOMImplementation.hpp> #include <xercesc/dom/DOMImplementationLS.hpp> #include <xercesc/dom/DOMNodeIterator.hpp> #include <xercesc/dom/DOMNodeList.hpp> #include <xercesc/dom/DOMText.hpp> #include <xercesc/dom/DOMAttr.hpp> #include <xercesc/parsers/XercesDOMParser.hpp> #include <xercesc/util/XMLUni.hpp> #include <xercesc/framework/XMLFormatter.hpp> #include <xercesc/util/XMLString.hpp> #include <stdlib.h> #include <string> #include <vector> #include <stdexcept> using namespace std; using namespace xercesc; class XMLStringTranslate; class CXML { public: CXML(); ~CXML(); XMLTransService::Codes tranServiceCode; void xmlParser(string&) throw(std::runtime_error); private: XMLStringTranslate *XMLTan; xercesc::XercesDOMParser *m_DOMXmlParser; //定义解析对象 };
class XMLStringTranslate : public XMLFormatTarget { public: XMLStringTranslate(const char * const encoding); bool TranslatorUTF8ToChinese(string &strTranslatorMsg); bool UTF8_2_GB2312(char *in, int inLen, char *out, int outLen); string translate(const XMLCh* const value); const XMLCh * const translate(const char * const value); virtual ~XMLStringTranslate(); protected: XMLFormatter * fFormatter; XMLCh * fEncodingUsed; XMLCh * toFill; char * m_value; protected: enum Constants { kTmpBufSize = 16 * 1024, kCharBufSize = 16 * 1024 }; void clearbuffer(); virtual void writeChars(const XMLByte* const toWrite , const unsigned int count , XMLFormatter* const formatter); }; #endif
//CXML.cpp #include <string> #include <iostream> #include <sstream> #include <stdexcept> #include <list> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <unistd.h> #include <iconv.h> #include "CXML.h " bool XMLStringTranslate::UTF8_2_GB2312(char *in, int inLen, char *out, int outLen) //码型转换 { iconv_t cd = iconv_open( "gbk ", "UTF-8 " ); // check cd if( (int)cd == -1 ) { cout < < "iconv is ERROR " < < endl; return false; } char *pin = in, *pout = out; int inLen_ = inLen + 1; int outLen_ = outLen; iconv( cd, &pin, (size_t*)&inLen_, &pout, (size_t*)&outLen_ ); iconv_close(cd); return true; } bool XMLStringTranslate::TranslatorUTF8ToChinese(string &strTranslatorMsg) { char* pstrSource = const_cast <char*> (strTranslatorMsg.c_str()); char pstrDestination[strTranslatorMsg.length()*2+1]; //如此处编译出错,可改为char *pstrDestination = new char[strTranslatorMsg.length()*2+1], 但要记住释放 memset(pstrDestination, '\0 ', strTranslatorMsg.length()*2+1); if(!UTF8_2_GB2312(pstrSource, strTranslatorMsg.length(), pstrDestination, strTranslatorMsg.length())) return false; strTranslatorMsg = pstrDestination; return true; } CXML::CXML() { try { // Initialize Xerces-C++ library XMLPlatformUtils::Initialize(); } catch(xercesc::XMLException & excp) { char* msg = XMLString::transcode(excp.getMessage()); printf( "XML toolkit initialization error: %s\n ", msg); XMLString::release(&msg); } XMLTan = new XMLStringTranslate( "utf-8 "); //创建 XercesDOMParser 对象,用于解析文档 m_DOMXmlParser = new XercesDOMParser; } CXML::~CXML() { try { delete XMLTan; XMLPlatformUtils::Terminate(); } catch(XMLException& excp) { char* msg = XMLString::transcode(excp.getMessage()); printf( "XML toolkit terminate error: %s\n ", msg); XMLString::release(&msg); } } void CXML::xmlParser(string & xmlFile) throw( std::runtime_error ) { //获取文件信息状态 struct stat fileStatus; int iretStat = stat(xmlFile.c_str(), &fileStatus); if( iretStat == ENOENT ) throw ( std::runtime_error( "file_name does not exist, or path is an empty string. ") ); else if( iretStat == ENOTDIR ) throw ( std::runtime_error( "A component of the path is not a directory. ")); else if( iretStat == ELOOP ) throw ( std::runtime_error( "Too many symbolic links encountered while traversing the path. ")); else if( iretStat == EACCES ) throw ( std::runtime_error( "ermission denied. ")); else if( iretStat == ENAMETOOLONG ) throw ( std::runtime_error( "File can not be read\n ")); //配置DOMParser m_DOMXmlParser-> setValidationScheme( XercesDOMParser::Val_Auto ); m_DOMXmlParser-> setDoNamespaces( false ); m_DOMXmlParser-> setDoSchema( false ); m_DOMXmlParser-> setLoadExternalDTD( false ); try { //调用 Xerces C++ 类库提供的解析接口 m_DOMXmlParser-> parse(xmlFile.c_str()) ; //获得DOM树 DOMDocument* xmlDoc = m_DOMXmlParser-> getDocument(); DOMElement *pRoot = xmlDoc-> getDocumentElement(); if (!pRoot ) { throw(std::runtime_error( "empty XML document " )); } // create a walker to visit all text nodes. /********************************************** DOMTreeWalker *walker = xmlDoc-> createTreeWalker(pRoot, DOMNodeFilter::SHOW_TEXT, NULL, true); // use the tree walker to print out the text nodes. std::cout < < "TreeWalker:\n "; for (DOMNode *current = walker-> nextNode(); current != 0; current = walker-> nextNode() ) { char *strValue = XMLString::transcode( current-> getNodeValue() ); std::cout < <strValue; XMLString::release(&strValue); } std::cout < < std::endl; *************************************************/ // create an iterator to visit all text nodes. DOMNodeIterator* iterator = xmlDoc-> createNodeIterator(pRoot, DOMNodeFilter::SHOW_TEXT, NULL, true); // use the tree walker to print out the text nodes. std::cout < < "iterator:\n "; for ( DOMNode * current = iterator-> nextNode(); current != 0; current = iterator-> nextNode() ) { string strValue = XMLTan-> translate(current-> getNodeValue() ); XMLTan-> TranslatorUTF8ToChinese(strValue); std::cout < <strValue < <endl; } std::cout < < std::endl; } catch( xercesc::XMLException& excp ) { char* msg = xercesc::XMLString::transcode( excp.getMessage() ); ostringstream errBuf; errBuf < < "Error parsing file: " < < msg < < flush; XMLString::release( &msg ); } }
XMLStringTranslate::XMLStringTranslate(const char * const encoding):fFormatter(0), m_value(0),fEncodingUsed(0),toFill(0) { XMLFormatTarget * myFormTarget = this; fEncodingUsed=XMLString::transcode(encoding); fFormatter = new XMLFormatter(fEncodingUsed ,myFormTarget ,XMLFormatter::NoEscapes ,XMLFormatter::UnRep_CharRef); toFill=new XMLCh[kTmpBufSize]; clearbuffer(); } XMLStringTranslate::~XMLStringTranslate() { if(fFormatter) delete fFormatter; if(fEncodingUsed) delete [] fEncodingUsed; if(m_value) free(m_value); if(toFill) free(toFill); fFormatter=0; fEncodingUsed=0; m_value=0; toFill=0; } void XMLStringTranslate::writeChars(const XMLByte* const toWrite , const unsigned int count , XMLFormatter* const formatter) { if(m_value) free(m_value); m_value=0; m_value=new char[count+1]; memset(m_value,0,count+1); memcpy(m_value,(char *)toWrite,count+1); } void XMLStringTranslate::clearbuffer() { if(!toFill) return; for(int i=0;i <kTmpBufSize;i++) toFill[i]=0; } [/i]string XMLStringTranslate::translate(const XMLCh* const value) //实现从 XMLCh* 到 string类型的转换 { *fFormatter < <value; string strValue=string(m_value); return strValue; } const XMLCh * const XMLStringTranslate::translate(const char * const value) { clearbuffer(); const unsigned int srcCount=XMLString::stringLen(value); unsigned char fCharSizeBuf[kCharBufSize]; XMLTranscoder * pTranscoder=(XMLTranscoder *)fFormatter-> getTranscoder(); unsigned int bytesEaten; unsigned int size=pTranscoder-> transcodeFrom( (XMLByte *)value, srcCount, toFill, kTmpBufSize, bytesEaten, fCharSizeBuf ); toFill[size]=0; string t1=string(value); string t2=translate(toFill); assert(t1==t2); return toFill; }
#ifdef MAIN_TEST int main() { string xmlFile = "sample.xml "; CXML cxml; cxml.xmlParser(xmlFile); return 0; } #endif //Makefile #tHIS IS MAKEFILE FOR XERCES-C++ APPLIACTION MAIN = xml CC = g++ CFLAGS = -c -g -Wall $(MAIN):CXML.o [TAB]$(CC) CXML.o -o xml -L/opt/ApacheXML/lib -lxerces-c CXML.o:CXML.cpp [TAB]$(CC) $(CFLAGS) -pedantic -I/opt/ApacheXML/include CXML.cpp -DMAIN_TEST .PHONY:clean clean: [TAB]rm CXML.o $(MAIN)
下面简要分析一下源程序:
首先,要想利用Xerces C++类库来解析XML文档,必须要对类库进行初始化,所以在类XML的构造函数中,我们对类库进行了初始化:XMLPlatformUtils::Initialize();
接下来,我们定义的解析对象,并在构造函数中对其进行了初始化操作,然后,在xmlParser函数中我们调用类库的解析函数接口,传人xml文件名(m_DOMXmlParser->parse(xmlFile.c_str()) ;)。因为在这里我们选用的是DOM方法,所以接下来我们需要创建DOM树:DOMDocument* xmlDoc = m_DOMXmlParser-> getDocument();,并获取DOM树的根节点 DOMElement *pRoot = xmlDoc->getDocumentElement()。
再接下来是什么呢?根据上面所说的,我们需要遍历这棵DOM树,因此我们需要一种遍历方法,在程序中我给出了两种遍历的方法,一种是创建遍历树 DOMTreeWalker *walker = xmlDoc->createTreeWalker(pRoot, DOMNodeFilter::SHOW_TEXT, NULL, true),还有一种是通过迭代器来遍历整棵DOM树 DOMNodeIterator* iterator = xmlDoc-> createNodeIterator(pRoot, DOMNodeFilter::SHOW_TEXT, NULL, true)。两种方法都可以达到同样的效果。程序中注释掉的代码是创建遍历树方法。
遍历完,并打印出节点值以后,我们需要终止对类库的调用,所以在析构函数中:XMLPlatformUtils::Terminate()。
解析简单xml文档的基本步骤就是如此简单,至于复杂的XML文档,解析的步骤,尤其是创建DOM树的方法有点不同,在这里便不作介绍。接下来,来讲一下困扰我多天的中文解析问题。我们知道,Xerces C++默认只支持节点名中文,至于节点值,属性值则不支持,即使解析出来的也是乱码,所以需要自己解决。在这里,我们选用UTF-8编码格式的XML文档。先来看一下乱码的原因,由于XML解析器解析的字符串都是 XMLCh*(typedef unsigned int XMLCh)格式的,一个字符占用一个字节,而汉字字符确要占用两个字节。故若不做适当的转换,汉字的输出结果就变成乱码了。在 http://www.vckbase.com/document/viewdoc/?id=738 提供了一种解决的方法,但是那个解决方案只有在locale环境为UTF-8的情况下你才可以看见正常的中文输出,在locale为GB18030等环境下,你的中文是乱码。但是在一种环境下可以正常显示,说明已经可以正常解析出来了,只是在不同环境的机器上需要进行码型转换,因此,我在他提供的类中又添加了两种方法,来进行码型转换:
bool TranslatorUTF8ToChinese(string &strTranslatorMsg); //实现从UTF-8到GBK、GB2312等码型的转换
bool UTF8_2_GB2312(char *in, int inLen, char *out, int outLen);
这样,你就可以在把UTF-8编码的中文正常的解析打印出来了。
xml4c ------http://www.alphaworks.ibm.com/tech/xml4c
IBM的XML Parser,用c++语言写就,功能超级强大。号称支持多达100种字符编码,能够支持中文,
适合于大规模的xml应用。若只是很小范围的应用,则非最佳选择,毕竟,你需要“背负”约12M左右的
dll的沉重负担。
Xerces c++ -------http://xml.apache.org/xerces-c
Apache的XML项目,同样是c++ 实现,来源于IBM的xml4c,因此编程接口也是和xml4c一致的。但是
目前只支持少数的字符编码,如ASCII,UTF-8,UTF-16等,不能处理包含中文字符的XML文档。
Xerces-C++ 是一个非常健壮的XML解析器,它提供了验证,以及SAX和DOM API。XML验证在文档类型定
义(Document Type Definition,DTD)方面有很好的支持,并且在2001年12月增加了支持W3C XML Schema
的基本完整的开放标准。
XMLBooster -------http://www.xmlbooster.com/
这个库通过产生特制的parser的办法极大的提高了XML解析的速度,并且能够产生相应的GUI程序
来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了另外一个可行的解决方案。