Xerces C++正确处理XML文档

1、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++)语言编写的)。
2、Xerces-C++: 功能介绍
        Xerces-C++是一个非常健壮的 XML 解析器,其提供的两种解析XML文档的方法,DOM和SAX (我是采用DOM方法)。
        SAX是一个面向事件的编程API.一个解析引擎消耗XML序列数据,并在发现进来的XML数据的结构时回调应用程序,这些回调称为事件句柄.
      与SAX不同,它允许对XML文档进行编辑并保存为一个文件或者流,还允许以编程的方式构建一个XML文档.DOM提供了一个内存中的模型,你可以遍历文档树,删除节点或者嫁接新节点.与解析的SAX事件不同,DOM事件反映出用户与文档的互动以及使用文档的改变.
       总的来说,SAX是按行遍历XML文档的,而DOM是先把XML文档生成树,然后遍历DOM树,来解析每个节点.
Xerces-C++:学习的过程
    2.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.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分钟的时间,所以要耐心等候)
        2.3、学习类库
        因为类库很大,所以刚开始,我并没有选择去分析与阅读类库,我是先在网上了一个比较完整的例子,然后对其进行编译和调试,然后从例子下手去分析类库所提供的接口。这里,我把自己的程序简化了一下,希望可以作为大家学习的例子。
        首先,我们需要定义一种 XML文档的样式。在这里,我们简单的定义一种样式(含有中文),如下:
//sample.xml
        
        <国家调查>
        
        
          
            china      111-> 江苏
            china      112-> 天津
            china      113-> 北京
            china      114-> 上海
            china      115-> 广州
           
         
            Asia      12-> 韩国
            Asia      13-> 日本
            Asia      14-> 越南
            Asia      15-> 柬埔寨
            Asia      16-> 老挝
         
         
            America    21-> 巴西
            America    22-> 阿根廷
            America    23-> 智利
            America    24-> 墨西哥
            America    25-> 巴拉圭
            America    26-> 美国
            America    27-> 加拿大
        
        
           Europe    31-> 英国
           Europe    32-> 意大利
           Europe    33-> 法国
           Europe    34-> 德国
           Europe    35-> 西班牙
           Europe    36-> 匈牙利
        
        THE END
        
        定义好格式后,我们来看看程序是如何实现对其解析的,程序如下:

CODE:
[Copy to clipboard]

//CXML.h
#ifndef XML_PARSER_HPP
#define XML_PARSER_HPP
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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(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 <               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 <        }

    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     toFill=0;
}
string XMLStringTranslate::translate(const XMLCh* const value)     //实现从 XMLCh* 到 string类型的转换
{
*fFormatter< 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编码的中文正常的解析打印出来了。

 

其他相关信息:

http://www.vckbase.com/document/viewdoc/?id=738

http://www.vckbase.com/document/viewdoc/?id=736

http://blog.csdn.net/ssyub/archive/2009/07/03/4320263.aspx

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值