您的位置:首页 > 编程语言 > C语言/C++

如何利用Xerces C++正确处理XML文档中的WhiteSpace

2007-10-25 15:43 701 查看
如何利用Xerces C++正确处理XML文档中的WhiteSpace
作者:冯键

提交者:eastvc 发布日期:2003-6-29 20:49:38
原文出处:http://www-900.ibm.com/developerWorks/cn/xml/tips/x-xercesc2/index.shtml
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);

[/code] 图 1 相关程序代码
<?xml version="1.0" encoding="UTF-8"?>
<personnel>
  <person id="Big.Boss" >
    <name><family>Boss</family> <given>Big</given></name>
    <email>chief@foo.com</email>
    <link subordinates="one.worker two.worker three.worker four.worker five.worker"/>
  </person>
</personnel>
                                    图 2 test.xmL

 
<?xml version="1.0" encoding="UTF-8"?>
<personnel><person id="Big.Boss" >  
<name><family>Boss</family><given>Big</given></name>
<email>chief@foo.com</email>
<link subordinates="one.worker two.worker three.worker four.worker five.worker"/>
</person>
</personnel>
图 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的问题。

4. 解决方法

基于上面的分析,我们首先需要为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++包中的文件:<xerces C++安装路径>/data/personal.xsd,相应地,test.xml中需要标注shema,详细见:<xerces C++安装路径>/data/personal-schema.xml文件,这里在图5中给出shema的声明部分。

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

<personnel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation='personal.xsd'>
        ... ...
</personnel>                               
                                  图 5 schema的声明

XMLPlatformUtils::Initialize();
... ...
DOMParser * Parser = new DOMParser;
Parser->setIncludeIgnorableWhitespace(false);
parser->setDoNamespaces(true);
parser->setDoSchema(true);  
parser->setValidationScheme(DOMParser::Val_Auto);
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 最终程序代码[/code]需要说明以下几点:

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的应用系统,相信在应用的过程中会遇到各种问题,欢迎有兴趣的朋友与我联系,共同交流。

Xerces C++ 2.2.0文档和例子程序

IBM developerWorker Xerces C ++技术论坛

冯键,如何利用Xerces-C++解析包含中文字符的XML文档, http://www-900.ibm.com/developerWorks/cn/xml/tips/x-xercesc/index.shtml

冯键,中科院计算机博士,主要从事计算机支持的协同工作(CSCW)、用户界面、消息中间件的研究,对XML编程有浓厚兴趣。可以通过 fjceline@vip.sina.com 于他联系。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: