Xerces-C++ 参考

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数据的结构时回调应用程序,这些回调称为事件句柄.
     与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文档的样式。在这里,我们简单的定义一种样式(含有中文),如下:
//sample.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编码的中文正常的解析打印出来了。
    XML文档的解析的样式很多,所以编出来的解析程序不可能达到通用的效果,不同的XML文档,解析的要求和方法都各有所异(如同时打印出你的节点名和节点值,上述的方法就不可行  ),所以还需要改动你的程序,所以如果你对解析XML有兴趣的话,可以深入的理解一下Xerces C++类库。

posted @ 2011-02-17 00:08 jingshaq 阅读(756) 评论(0) 编辑

Xerces-C++ 是一个非常健壮的 XML 解析器,它提供了验证,以及 SAX 和 DOM API。XML 验证在文档类型定义(Document Type Definition,DTD)方面有很好的支持,并且在 2001年12月增加了支持 W3C XML Schema 的基本完整的开放标准。

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++)语言编写的)。

IBM 仍然在 Xerces-C++ 的基础上继续 XML4C 项目。从我所研究的版本来看,XML4C 与 Xerces-C++ 相比突出的好处是,它的默认安装对大量国际字符编码提供了更好的支持(见 参考资料)。

验证

指定 XML 文档资料结构的两种基本方法是 DTD 和 W3C XML Schema,其中 DTD 的历史要长得多。XML Schema 基本上就是表示为 XML 的 DTD。Xerces-C++ 提供了很好的默认安装的验证能力以保证 一个 XML 文档符合一个 DTD。

许可证

Xerces-C++ 的使用需要遵守 Apache Software License (见 参考资料),它正巧是最具有可读性的开放源代码许可证之一。它可以与 BSD 许可证作一个很好的对比。实质上,不付特许使用费 就可以在您(或您公司)的软件中使用 Xerces-C++,只要向客户和用户说明软件中包括 Apache 代码,并加上适当的版权说明即可。关于许可证的具体内容 请参见 Web 页面。


SAX: 事件 API 模型

正如您可能知道的,SAX 是一个用于解析 XML 文档的面向事件的编程 API。一个解析引擎消耗 XML 序列数据,并在发现进来的 XML 数据的结构时回调应用程序。这些回调称为事件句柄。SAX 实际上是两个 API:SAX 1.0 是最初的,而 SAX 2.0 是当前修订过的规范。它们两个很类似,但是也有区别,因此大多数基于 SAX 1.0 的应用程序在移植到新规范后会失败。

,SAX API 规范被作为一个单独的项目移植到了 SourceForge(见 参考资料)。本文 后面给出的 SAX 例子使用的是 SAX 2.0。


DOM: 文档对象模型

与 SAX 不同,DOM API 允许对 XML 文档进行编辑并保存为一个文件或者流。它还允许以编程方式从头开始构建一个新的 XML 文档。 其原因是 DOM 为文档提供了一个内存中的模型。 您可以遍历文档树、删除节点或者嫁接新节点。

tech wrecks

DOM 是 W3C 技术推荐中的一员,被亲切地称之为 tech wrecks。DOM 有 3 级,第 1 级和第 2 级为完全技术推荐状态,而第 3 级为工作草案状态。

第 1 级 DOM 的核心定义了基本 XML 功能所需要的大部分内容:构建 XML 文档表示的能力。 DOMString 类型被显式指定包括宽 UTF-16 字符。第 1 级还定义了与 DOM 树不同部分以编程方式互动的接口。在第 1 级中有意去掉了 XML 的序列化。在第 1 级核心之外是第 1 级 DOM 的 HTML 定义。这部分内容试图用早期的 Dynamic HTML 对象模型解析第 1 级 DOM 的核心(不确切地称为第 0 级)。

第 2 级 DOM 增加了命名空间、事件和迭代器,以及视图和样式表的支持。一些应用程序需要第 2 级 DOM:例如,为一个命名空间指定一个 XML Schema 对于像 RDF 这样的应用程序是很重要的,在这样的应用程序中,XML 标记来自不同的架构,很有可能出现命名冲突。第 2 级对 DOMImplementation 接口增加了两个 createDocument方法。有一个例子显示为什么它是重要的。当您认为不会再在 SAX 中 发现回调和事件句柄时,它们又出现在 Event 接口中。与用于解析的 SAX 事件不同,DOM 事件可以反映出用户与文档的互动以及对使用文档的改变。反映文档结构改变的 DOM 事件称为 mutation events。 TreeWalkers 和 NodeIterators 增强了 DOM 树的遍历。程序可以通过 StyleSheet 接口检查样式信息。最后,视图支持使 XML 应用程序可以检查原始的和经过样式表处理过的 这两种形式的文档。在此之前和之后的视图分别称为 document视图和 abstract视图。

第 3 级 DOM 核心对 DOMImplementation 接口增加了 getInterface 方法。在第 3 级文档中,可以指定文档的字符编码或者设置一些基本的 XML 声明,如 version 和standalone 。第 2 级不允许将 DOM 节点从一个文档移动到另一个文档。第 3 级取消了这种限制。第 3 级增加了 user data——可以选择性地附加到任何节点上的额外的应用程序数据。第 3 级还有一些其他高级特性,但是 W3C 委员会仍然在完善第 3 级草案。参见 参考资料中的链接以了解委员会的进展。


下载和安装

可以下载压缩后的 tar 形式的 Xerces-C++,也可以下载预编译的二进制文件(见 参考资料)。通过 Perl、Python、VBScript 或者 JavaScript 访问库的脚本用户可以下载适用于他们平台的二进制文件以进行安装。C++ 程序员很可能愿意从 源压缩文件编译自己的二进制文件。在 Apache XML 组 Web 站点上有很好的编译指导,在本文稍后的地方我会讨论我所发现的几个微妙的问题—— pthreads 链接问题和修复 Windows 平台上潜在的 内存泄漏 问题。第 2 部分将包括在 SVG 例子中指定 DOCTYPE 的提示。如果希望在阅读时 编译库,那么要先看看 Apache 站点上的 Xerces 编译文档(见参考资料),然后回到这里了解如何将 Xerces 链接到自己的应用程序。

可以下载 tar 形式的文件并脱机操作(比如用笔记本电脑)。tar 文件中包括了全部 HTML 文档,所以不需要回到 Web 站点去看指示。

Win32 版本上的编译

在 Visual Studio dot-NET 或者 Win64 上安装软件的步骤与在 Win32 上的编译步骤一样。

  1. 解压缩 Xerces 源 tar 文件到一个工作目录。Xerces-C++ 有自己的目录结构,所以应保证在这一步中保持相对路径名。
  2. 用 Windows 资源管理器或者习惯使用的文件管理器进入到 \\xerces-c-src_2_3_0\\Projects\\Win32\\VC6\\xerces-all\\ 文件夹并单击xerces-all.dsw workspace 文件以启动 Microsoft Developer Studio。 
    注:这些指导假定您是在 Visual Studio 6 中编译 Win32 应用程序。对于 Visual Studio dot-NET 或者 Win64 应用程序,在 Win64 或者 VC7 各自的目录中重复步骤 1和2。
  3. 在 Developer Studio 中,让 XercesLib 成为当前活跃的项目,并按 F7 以编译 DLL。对于去年的硬件,这需要一到两分钟。
  4. 在您的项目中增加到 Xerces 头文件的路径(要链接到 Xerces-C++ 的应用程序需要在它们的工作空间中包括 XercesLib DSP 项目文件,或者在它们的项目文件中增加 LIB 文件以允许链接)。选择 Project>Settings 以调出项目设置对话框。从 Settings组合框中选择 All Configurations,单击 C++ 标签,选择 Preprocessor类别,并在 Additional include directories 文本框中添加 Xerces 包含路径(类似于 \\xerces-c-sr2_2_0\\src )。
  5. 如果在工作空间中添加了 XercesLib DSP,记得要将自己的项目标记为依赖于 XercesLib 项目,否则,就会得到链接错误。
  6. 编译一个 stub C++ 源文件,该文件不做任何事情,只包含一行内容用于读取 #include <xercesc/sax/HandlerBase.hpp> 。如果 能够编译这个只有一行的 C++ 文件,那么您的包含路径就可能是正确的。之后保存工作空间。为了运行和调试这个应用程序,在工作目录中放入 Xerces DLL 的一个副本。

Linux 下的 编译

按照 doc/html 文件夹中的详细指导编译 Xerces-C++ 共享库。下面的命令展示了如何用压缩的源文件编译 Xerces-C++ 库。这里假定在像 /home/user 这样的目录中有xerces-c-src_2_3_0.tar.gz 文件。不管选的是什么目录,它都应该与XERCESCROOT 变量匹配,因为 configure 脚本有这个要求。

# cd /home/user
# gunzip xerces-c-src_2_3_0.tar.gz
# tar -xvf xerces-c-src_2_3_0.tar
# export XERCESCROOT=/home/user/xerces-c-src_2_3_0
# cd $(XERCESCROOT)/src/xercesc
# ./configure
# make all

对于本例后面的部分,我假设源树是在 /home/user/xerces-c-src_2_3_0 目录中。如果一切顺利,共享库应该出现在 lib 文件夹中。如果有问题,那么请参考/doc/html 文件夹中的编译指导。这时,您可以将这个库 (和 symlinks)拷贝到/usr/lib ,或者定义相应的环境变量以使装载器可以找到新编译的库。

测试新库的方便方法是编译并运行一个例子:

# export XERCESCROOT=/home/user/xerces-c-src_2_3_0
# cd $(XERCESCROOT)/samples
# ./configure
# make all

我在一个全新安装的 Slackware Linux 9.0 上编译其中一个例子中遇到了一个小问题。链接器抱怨缺少与 pthread 相关的输出。我编辑了 Makefile.in 文件以包括对-lpthread 的引用并再次运行 configure 。第二次时键入 make all 就可以了。

证明库可以工作后,就可以开始自己的 Xerces-C++ 项目了。使用 -I 编译器选项以帮助编译器找到 Xerces 头文件。用链接器选项 -L 和 -l 以帮助链接器找到 Xerces-C++ 库。清单 1 给出了一个可以使用的最简单的 makefile 以供开始。


清单 1. 最简单的 makefile
APP = example
XERCES = /home/user/xerces-c-src_2_3_0
INCS = ${XERCES}/src
${APP} :: ${APP}.cpp
${CC} -lxerces-c-src_2_3_0 -I${INCS} ${APP}.cpp -o ${APP}

使用清单 1 的命令是 make 或者 gmake 。可以将 APP 变量改变为任何您所使用的源文件。本文中的例子使用类似的 makefiles。

Xerces C++ 从版本 2.2.0 开始增加了对 C++ 命名空间的支持(不要与 XML 命名空间搞混)。如果有工作于 2.1.0 的代码,并且希望利用新版本的好处,那么在代码中包括 Xerces C++ 头文件的后面添加下面三行。


清单 2. Xerces C++ 命名空间支持
#ifdef XERCES_CPP_NAMESPACE_USE
XERCES_CPP_NAMESPACE_USE
#endif

当然,可以仅仅对所有 Xerces-C++ 对象加上 XERCES_CPP_NAMESPACE:: namespace 前缀。


示例应用程序

为了在解释有关使用 Xerces-C++ 的基本内容时能够有趣一些,我将用XML作为数据格式创建一个简单条形图。为了避开跨平台项目中的平台 GUI 特定障碍,我用 ASCII 艺术制作这个条形图。不管怎么说,这是一 篇有关 XML 而不是 GTK、OpenGL 或者 Direct-X 的文章。如果对于使用图形数据的 XML 表示有兴趣,可参见 SVG 和 SMIL (见 参考资料)。我在第 2 部分描述的 DOM 例子输出 SVG。我将以简单的文本应用程序开始。

清单 3 是数据的 DTD。下面我将构建一个程序以装载这些数据,确定使用什么比例、然后实际在屏幕上描绘这个数据。


清单 3. 示例应用程序数据的 DTD
APP = example
<?xml version="1.0" ?>
<!ELEMENT figures (PCDATA) >
<!ATTLIST figures type (sales | inventory | labor) >
<!ATTLIST figures value CDATA >
<!ELEMENT department (figures*) >
<!ATTLIST department name CDATA>
<!ELEMENT corporate (department*) >
<!ATTLIST corporate name CDATA >

清单 4 显示了数据可能的样子。


清单 4. 示例输入 XML 数据
APP = example
<?xml version="1.0" ?>
<corporate name="Big Biz">
<department name="North">
<figures type="sales" value="125000.00"/>
<figures type="inventory" value="90000.00"/>
<figures type="labor" value="110000.00">estimated</figures>
</department>
<department name="South">
<figures type="sales" value="980000.00"/>
<figures type="inventory" value="110000.00"/>
<figures type="labor" value="115000.00">estimated</figures>
</department>
<department name="East">
<figures type="sales" value="210000.00"/>
<figures type="inventory" value="80000.00"/>
<figures type="labor" value="95000.00">estimated</figures>
</department>
<department name="West">
<figures type="sales" value="160000.00"/>
<figures type="inventory" value="75000.00"/>
<figures type="labor" value="130000.00">estimated</figures>
</department>
<department name="Central">
<figures type="sales" value="723000.00"/>
<figures type="inventory" value="11000.00"/>
<figures type="labor" value="221000.00">estimated</figures>
</department>
</corporate>


SAX2 实现

清单 5是基准 SAX 实现。这不是一个完整的程序,因为它缺少句柄实现,但是它的确显示了使框架就序所需要的东西。对 XMLPlatformUtils:Initialize() 和XMLPlatformUtils::Terminate() 的调用非常重要。在应用程序不能正确初始化库时,库会抛出一个异常来提供保护。

为了使清单 5 中的程序成为完整的应用程序,需要添加 清单 6中的事件句柄类。SAX2 带有名为 DefaultHandler 的默认事件句柄类,它在同名的 C++ 文件中定义。默认句柄什么也不做——它只是一个 stub 实现——但是它是完整的, 所以我在这里用它作为图形事件句柄类的基类。

清单 7中的这个文件是 清单 6中的事件句柄类的实际实现。虽然程序其他部分只是让 SAX2 解析器运行的样板代码,但是清单 7 中的部分定义了应用程序的个性。

Xerces-C++ 使用 XMLCh 作为 typedef'd 字符表示。在一些平台上, XMLCh 与 C类型wchar_t 兼容, 后者通常是两个——但是有时是四个——字节宽。因为这种宽度不固定的可能性,所以文档不鼓励 wchar_t 与 XMLCh 互换的做法。在一些平台上这没问题,但是在另一些平台上就会出错。Xerces-C++ 使用更大的字符表示 UTF-16 而不是 UTF-8 或者 ISO-8859 交换文本。为了调试这个程序,我使用了XMLString::transcode 函数来转换将在控制台上显示的宽字符字符串,如图 1 所示。


图 1. SAX 解析器输出的截屏
Screen shot of SAX parser output

我发现在 Microsoft Windows 中使用 Xerces 内部字符串类有一个问题。在XMLString.hpp 中的注释要求调用 replicate 和其他类似的函数以释放返回的内存。问题出现在将应用程序与作为 DLL的 Xerces-C++ 库进行链接上。字符串在 DLL 的本地堆上分配。如果应用程序和 XercesLib DLL 都使用同一个 C 运行时(CRT)库 DLL,那么一切正常。但是,如果应用程序使用单线程的 CRT,而 XercesLib 使用多线程的 CRT,那么 DLL 问题就会出现。当应用程序试图释放字符串内存时,C 运行时注意到内存不是来自于应用程序的本地堆。在调试编译时它会抛出一个异常,但是在发布编译时它可能无警告地泄漏内存。在以前版本的 Xerces(如 1_5_1)的示例程序是通过不释放内存来避免这个问题。

我解决这个问题的方法是在 XMLString 类中增加两个静态放弃函数。因为字符串内存是由在 DLL 内部执行的代码释放的,所以使用的是正确的本地堆,没有调试断言结果。我高兴地看到 Xerces 开发者 Tinny Ng 将它添加到了 XMLString 类中,并更进一步设字符串指针为 null(见 参考资料)。这样做的另一个好处是程序员不需要担心XMLString 是如何分配内存的。不需要猜测它们应该使用 delete[] 还是 free ,它们只要调用 XMLString::release 就行了。当然,也可以只是确保您的应用程序要使用的 CRT 与 XercesLib DLL 使用的 CRT 一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值