Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9727173
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-04-02 15:01:57

下载本文示例代码

Apache的Xerces C++ 和IBM的XML4C是广大C/C++编程人员非常喜欢使用的XML解析器,我比较偏好XML4C,最主要的原因是它能正确处理XML文档中的中文字符,具体可参见我以前在IBM developerWorkers China上发表的文章《如何利用Xerces-C++解析包含中文字符的XML文档》。

Xerces C++提供DOMParser和SAXParser解析XML文档,主要用途可有以下三种:

  • 生成DOM_Document,并调用Xerces C++的API操纵内存中的XML Tree;
  • 与XMLFormat等其它类结合,格式化XML文档,生成包含WhiteSpace的缩进式XML,方便使用者阅读;
  • 去除XML文档中多余的whiteSpace,紧缩XML文档,应用于以XML为数据格式实现消息传送的应用,尽可能降低XML传输占用的网络带宽。

第2和第3种应用的实现比较简单,可参照Xerces C++提供的DOMPrint和SAXPrint的例子,并略做改进即可解决。而对于应用非常普遍的第一种情况,常有人会因为XML文档中的whitespace而出现程序处理问题,不能正确操纵DOM_Document表示的XML tree,从而影响了Xerces C++或XML4C上的应用开发。

下面以Xerces C++的DOMParser为例,描述当解析含whitespace的XML文档时存在的问题,图1是部分程序代码,图2是要解析的XML文档。

图1中的代码在创建DOMParser之后,调用了DOMParser类的setIncludeIgnorableWhitespace()方法,目的是告诉解析器不要在DOM_Document中包含whitespace,因而,根据图2所示,图1代码运行结果应为:

List size = 1

但是,程序实际运行的结果为:

List size = 3

对DOM_NodeList遍历时发现,DOMParser解析test.xml时将文档中的whitespace认为了DOM_Text节点。DOMParser没有理会setIncludeIgnorableWhitespace()方法的调用,这样,DOM_Document中会存在多个代表whitespace的DOM_Text节点,这不仅占用了多余内存,而且,极大影响了程序员对DOM_Document的操作,甚至会使子节点是否等于某值的判断始终为false。因而,Xerces C++的程序员需要在DOM_Document中过滤掉所有的whitespace,如果将test.xml中多余的whitespace去掉(见图3所示)后,重新运行图1的代码,结果与预想的相同。


XMLPlatformUtils::Initialize();
... ...
DOMParser * Parser = new DOMParser;
Parser->setIncludeIgnorableWhitespace(false);
Parser->parse( "test.xml" );

DOMDocument  doc = Parser->getDocument();
DOM_Element root = doc.getDocumentElement();
DOM_NodeList List = root.getChildNodes();
Int len = list.getLength();
printf("\n list size = %d", len);
    

图 1 相关程序代码





  
    Boss Big
    chief@foo.com
    
  


图 2 test.xml




   
BossBig 
chief@foo.com




图 3 去除whitespace后的test.xml

显然,利用额外的程序编程解决此问题是不明智的。Xerces C++应提供了相应的机制来解决它。我用google搜索了这方面的信息,发现还是有许多人遇到了此类问题,尽管IBM论坛上有人提出了解决思路,但是,还不够完整,我在研究Xerces C++的相关资料和代码解决此问题后认为,如何利用Xerces C++正确处理xml文档中的whitespace问题需要有较详细的解释和解决方法,权当是抛砖引玉吧,希望能为Xerces C++或XML4C的普及应用有所帮助。

根据void DOMParser::setIncludeIgnorableWhitespace (const bool include ) 的文档说明,Parser是否包含whitespace的设置仅在Parser对XML文档进行有效性验证处理时有效。因而,图2中的test.xml只能是格式良好的XML文档,由于它没有相应的schema定义,所以,DOMParser无法对此文档进行有效性验证,缺省认为whitespace是DOM_Document的子节点,类型是DOM_Text。

那么,为test.xml提供schema之后,图1的运行结果是否正确呢?答案是不正确的,DOMParser还需要调用下列的API来设置其它选项。

方法名 方法说明
Void SetDoSchema(const bool newState ) 设置Parser是否处理xml文档中的schema,如果为true,则Parser还要处理xml中的schema,否则,parser不处理xml文档的schema。
Void setDoValidation ( const bool newState ) 此方法同setValidationScheme,不推荐使用。
Void setDoNamespaces ( const bool newState ) 设置Parser是否处理xml文档中的名域,如果为true,则Parser增强名域定义的约束和规则。
Void setValidationScheme (const ValSchemes newScheme ) 设置Parser利用定义的schema对xml文档进行有效性验证解析。NewScheme的值有Val_Never,Val_Auto,Val_Always,分别表示对xml文档不进行shema有效验证、自动选择是否验证、总是进行有效验证。

因而,通过设置DOMParser的几个选项并提供test.xml的schema就应该能解决whitespace的问题。

基于上面的分析,我们首先需要为test.xml提供schema定义,这是解决此问题的第一步,也是必须的,如果不提供schema定义而想完成xml文档的紧缩处理,则需要程序员额外增加实现代码;或者使用SAXParser,在实现ContentHandler::ignorableWhitespace(const XMLCh* const chars, const unsigned int length)的纯虚方法中特殊处理,不为whitespace生成DOM_Text节点。但我这里不推荐此种处理方法,XML文档的有效性验证在许多应用系统中是必须的。

Test.xml的schema文件定义见Xerces C++包中的文件:\data\personal.xsd,相应地,test.xml中需要标注shema,详细见:\data\personal-schema.xml文件,这里在图5中给出shema的声明部分。

在上述步骤完成后,修改图1的程序代码,设置Parser支持XML文档的有效性验证,具体见图6。



... ...


图 5 schema的声明



XMLPlatformUtils::Initialize();
... ...
DOMParser * Parser = new DOMParser;
Parser->setIncludeIgnorableWhitespace(false);
parser->setDoNamespaces(true);
parser->setDoSchema(true);   
parser->setValidationScheme(DOMParser::Val_Auto);
 
parser->parse( "test.xml" );

DOMDocument  doc = Parser->getDocument();
DOM_Element root = doc.getDocumentElement();
DOM_NodeList List = root.getChildNodes();
Int len = list.getLength();
printf("\n list size = %d", len);

图 6 最终程序代码

需要说明以下几点:

  • XML文档和shema定义中都用到了名域,如图5中的xsi,schema定义中的xsd:element等等,所以,parser一定要设置名域的支持(调用setDoNamespaces(true)),否则,parser在解析xml的过程中会抛出异常。
  • setDoValidation()方法和setValidationScheme()方法在同一程序中不应同时出现。
  • 注意这些方法调用之间的相互关系,例如,如果设置了对xml进行有效验证(调用setValidationScheme),而没有设置Parser对schema的处理支持(没有调用SetDoSchema),这时,DOMParser会在处理过程中抛出异常。
  • 最好选用Val_Auto设置DOMParser的有效性验证,由Parser根据XML中是否标记schema来确定是否需要有效性验证,这种处理会使程序较为灵活。

目前,许多企业已经或者正在采用Xerces C++开发XML的应用系统,相信在应用的过程中会遇到各种问题,欢迎有兴趣的朋友与我联系,共同交流。

下载本文示例代码
阅读(2411) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-04-28 15:20:30

请教个问题:我现在用XQILLA实现了节点定位,而后想修改节点的内容,却报出异常document has changed。是否意味着xqilla不能修改DOM树,那应该如何实现这个功能呢?谢谢