您的当前位置:首页正文

MSXML应用总结

2022-05-23 来源:易榕旅网


MSXML应用总结概念篇

微软提供了大量的XML开发工具和技术,而SMXML(Microsoft XML Core Services)应该是一般开发中最常用到的技术了。MSXML支持了包括DOM(Document Object Model)、SAX(Simple API for XML)、XMLHttpRequest、XPath、SOM(Schema Object Model)等多种API接口和XML操作工具。本篇先主要介绍应用MSXML所涉及到的概念及说明,下一篇将总结一下应用MSXML DOM SDK进行XML文档处理。 一、MSXML版本说明

目前共有四种MSXML版本,分别为MSXML3、MSXML4、MSXML5和MSXML6。由于在微软的Windows系统中,MSXML主要是用来支持浏览器和Office的,因此这些版本多与不同浏览器版本及Office版本相关。微软推出IE6时带的是MSXML3,因此一般在XP系统上是支持MSXML3的,MSXML3也成为应用最广泛的一个版本。MSXML5主要用来支持Microsoft Office;而MSXML4很快就被更新版本的MSXML6替代了,微软推荐首选应用最新的MSXML6,其次应用MSXML3。

二、XML(Xtensible Markup Language)简介

要学习MSXML首先要了解XML。XML是一种被设计用于网络环境或跨平台环境数据存储和交换的标记性语言,达到数据存储分析一致性的目的。XML的结构形式和大家熟悉的HTML类似,但两者的用途没有关联,最大的区别在于XML用来存储数据,并允许使用者定义自己的元素。XML内容灵活的可扩展性以及简单易用的结构设计,使它发展非常迅速,目前已是各种应用环境下的程序之间进行数据传输和交换的最流行技术,另外在信息存储和描述领域,XML也逐渐成为应用标准。

关于XML的术语大家可以去专门的学习网站了解,这里只简单的说明一下XML的文档结构。先看一个xml文档示例。

文本

子节点

第1行为XML声明,定义XML使用的版本和编码。如果XML中的编码方式不是UTF-8或UTF-16,则必须声明编码方式,否则有可能解析失败。关于XML编码的问题下面会详细介绍。

第2行是注释,形式跟HTML是一样的。 第3行的root为根元素(文档元素),其他所有元素都包含在该元素的开始和结束标签之间。XML文件必须要有一个根元素,其他元素都出现在根元素内部。 第4行到第10行为根元素的子节点元素,这些元素是可以重名的。一个元素可以拥有属性、文本和子元素。如第4行的item元素拥有\"type\"属性,item标签之间的\"文本\"就是该元素的文本,第8行的item则拥有子元素\"subitem\"。

第6行的段是一个特殊的语法,被称为CData段。表示其中的字符无需解析,例如一些类似'<'、'/'、'>'的特殊字符,这样可以防止XML的结构被破坏。 另外需要注意的一些事项:

1、XML的标签是大小写敏感的;

2、一些转义字符在表达非转义的情形下,应使用实体引用,如将'<'变为'<',将'>'变为'>',将'&'变为'&',将'''变为'&apos',将'\"'变为'"'; 3、XML中应尽量少使用属性,而多使用子元素。 三、XML中的编码

计算机上的字符集问题是一个让所有开发者头疼的问题,由于历史原因以及具体应用的不同需求,产生了很多种字符编码集,常见的大概可以分为两种:单字节编码和多字节编码,前者代表为ASCII,后者代表为Unicode。但是除了这两者之外,还有很多字符集,而且单Unicode就提供了3种编码方法:UTF-8,UTF-16和UTF-32。关于字符集的具体知识,大家可以自己去查阅一些资料。

XML对字符编码的规定是:如果XML指定编码格式,则按照指定格式进行处理,当然此时必须保证文档存储的字符编码格式与指定的统一,或者解析器能够处理这种编码格式,如果出现错误,解析器会给出提示信息。当XML没有指定编码格式时,采用默认编码格式UTF-8,如果此时文档编码格式不是UTF-8,那么解析就会出错。 四、DOM(Document Object Model)

DOM是MSXML的一种对XML文档进行处理的标准模型,将XML文档全部加载到内存中形成一个树结构,在此基础上将XML当作一个对象进行操作。DOM提供了一系列的API并为XML结构中的各种类型元素定义了对应的对象接口。利用这些接口,可以对XML文档进行创建、遍历,对文档内容进行添加、删除、修改等动态操作。 关于DOM的应用将在下一篇中详细说明。 五、SAX(Simple API for XML)

看名字可以知道SAX是对XML操作的一个API集合,为什么这个集合\"Simple\"呢?其实是相对于DOM来说的。

上面已经介绍,DOM模型每次都会将整个XML文档装载到内存当中维护一个树结构,可想而知,当XML文档比较复杂或者体积比较大的时候,维护起来肯定会影响到效率。因此,当开发者比较重视效率时,应用SAX可能是一个好的选择。

SAX最大的特点就是事件驱动。装载XML文件时,SAX遍历文档并产生诸如开始和结束解析元素之类的事件,通知外部应用进行处理。所有的处理都在一次遍历中完成,因此SAX处理XML文档效率很高。另外针对大型的文档,SAX可以每次只将一部分装入内存,这样无论在空间效率和时间效率上都能得到有效提高。

当然SAX的应用范围是会受到一些限制,而且外部的处理也非常琐碎,总之与DOM应该是很好的互补。 六、XPath

Xpath是一种能够在XML文档中寻找信息的语言,它通过XML文档中的元素和属性来进行导航,形式非常类似我们平时用的路径表达式。

XPath内置了很多函数可以帮助实现导航功能,通过指定路径表达式来选择XML文档中的节点或节点集合。在XPath中有七种节点:元素、属性、文字、命名空间、处理说明、注释和根节点。具体的路径表达式规则大家可以参考相关资料。 七、MSXML API版本 这一节是最新加进来的,因为上面第一节说了一下MSXML的版本,但是还有一个MSXML API的版本,这两者比较容易混淆,在此说明一下。第一节说的是MSXML dll库文件的版本,而

API的版本是另一套规则。历史版本有:MSXML1.0、MSXML1.0 SP1/SP2、MSXML2.0、MSXML2.6、MSXML3.0、MSXML4.0、MSXML5.0 for Microsoft Office Applications,这些版本中较早的MSXML1.0和MSXML1.0 SP1/SP2现在都已不再支持,MSXML2.0是处理XML文档最常用的版本,后面的版本都是添加了一些新的功能接口。

基本的概念就这些了,希望对大家了解MSXML有所帮助。 MSXML应用总结开发篇(上)

(由于包含太多格式符号,新浪提示篇幅过长,因此分为上、下两篇) 本篇是接前文\"MSXML应用总结概念篇\"写的,主要总结一下MSXML DOM接口的应用。DOM(Document Object Model)是微软提供的处理XML文档的一个API标准库,我们可以将其理解为一组抽象了XML文档结构的接口。

MSXML的DOM模型是符合W3C DOM标准的,而DOM API在Windows中以COM接口的形式提供,关于COM请大家查阅相关资料。简单来说,COM提供了一个环境和一套规则,使接口的设计实现到对象的创建、使用和释放都标准化,从而使COM支持跨平台和跨语言;更重要的是,遵守COM规范使我们代码的接口与实现分离,将程序框架的稳定与扩展统一起来,对于使用COM接口的人则更加简单直观。COM中一个很重要的概念是refcount,即接口对象的访问计数,通过AddRef和Release两个接口函数来控制。要想用好refcount还是件较困难的事情,因此我推荐大家使用智能指针。使用智能指针就像使用一个简单指针一样,我们完全不用去关心指针指向内存空间的释放。 本篇总结采用API版本是MSXML2.0。 首先我们看一下常用的接口:

IXMLDOMDocument:XML文档接口,DOM树结构的根结点,是对文档访问和操作的入口;

IXMLDOMNode:节点接口,该接口是普遍意义上的节点接口,很多类型节点接口都从它派生,包括IXMLDOMDocument;

IXMLDOMNodeList:节点列表接口,表示一组关联的节点集合;该列表中的node元素通过index(从0开始)访问,另外该接口中的元素还是动态的,会随着XML文档的改变而更新;

IXMLDOMNamedNodeMap:节点集合接口,也表示一组关联节点的集合;不过与list不同的是,该集合是无序的,该接口常用于表示节点的属性集,并且该接口也是动态的; IXMLDOMElement:元素接口,一般用来表示一个节点及其属性; IXMLDOMAttribute:节点属性接口,对节点属性进行访问和操作; IXMLDOMText:节点中文本控制接口;

IXMLDOMComment:XML文档中的注释接口;

IXMLDOMParseError:出错处理接口,包括了错误的详细信息。

以上都是最常用的DOM接口,还有一些接口没有在此列出。对于接口来说,都有相应的智能指针接口,一般为接口名加上Ptr,比如IXMLDOMDocument的智能指针接口为IXMLDOMDocumentPtr。这里有一个接口继承关系示意图:

在VS2005环境下进行DOM应用开发,首先要设置DOM接口应用环境,在stdafx.h文件中加入语句:

#import raw_interfaces_only 如果你的系统文件夹下有msxml6.dll文件,#import语句将成生MSXML库类型信息,一般会在你的工程编译文件夹下生成msxml6.tlh和msxml6.tli两个文件,打开看一下可知这两个文件包含了一些COM接口类型及函数的声明以及一些库信息。实际上,#import指令使dll库中的类型信息导出为描述的COM接口的c++类头文件。而\"raw_interfaces_only\"属性使

得生成文件只有msxml6.tlh一个,而且接口函数只有HRESULT返回类型一种形式,且省去了raw_前缀;如果去掉该属性,则除了在msxml6.tlh文件中声明带raw_前缀的返回HRESULT类型的接口函数外,还会在msxml6.tlh中生成不带raw_前缀的wrapper接口函数,并在msxml6.tli文件中生成返回接口指针类型的wrapper接口函数。因此我们在应用DOM接口的时候,发现有两套完成相同功能的接口函数,分别返回HRESULT类型和接口指针类型,就是因为上述原因,这应该是Windows环境下COM接口描述的规则,比较深入的介绍请参考这篇文章:http://www.cnblogs.com/xiaotaoliang/archive/2005/07/20/196257.html。为了应用方便,我们下面的示例代码不一定用的都是加了\"raw_interfaces_only\"属性后的接口函数,建议大家可以去掉该属性,此处只是加以说明。

另外一种加载DOM接口的方法是直接在工程环境中添加msxml库的路径,并链接msxml6.lib文件,这里不再详述。 设置DOM环境后,还要初始化COM应用环境,在应用线程初始化函数中调用CoInitialize,并在线程退出时调用CoUninitialize。

现在我们可以用DOM接口来对xml文件进行操作了,我将按操作分类进行总结。 一、xml文件的加载和保存

由于DOM模型面向的是整个xml文件,因此我们需要自己创建的接口只有IXMLDOMDocument一个,其他接口都是从它直接或间接得到的,xml文件的加载和保存函数也在IXMLDOMDocument接口中实现。创建IXMLDOMDocument接口的代码如下: MSXML2::IXMLDOMDocumentPtr pXmlDoc;

HRESULT hr = pXmlDoc.CreateInstance( __uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER); if( FAILED(hr))

printf(\"Failed to create DOM document interface pointer.\\n\"); 加载xml文件代码为: try {

pXmlDoc->async = VARIANT_FALSE;

pXmlDoc->validateOnParse = VARIANT_FALSE; pXmlDoc->resolveExternals = VARIANT_FALSE; if( pXmlDoc->load(\"test.xml\") != VARIANT_TRUE) {

printf(\"Fail reason: %s\\n\ } else {

// success } }

catch(_com_error errorObject) {

printf(\"Exception, HRESULT = 0x%08x\ }

上面代码中,开始3句是设置IXMLDOMDocument接口的3个属性值。

async表示调用的阻塞模式,为true时为异步,此时load函数调用立即返回,而不管文

件加载是否完成;为false时为同步模式,即在加载完之后函数返回。在异步模式中,可以通过查询readyState属性值来判断是否加载完毕,也可以设置onreadystatechange handler或者onreadystatechange event进行处理。async的默认值为true。

validateOnParse表示当xml文件结构有错误时是否继续进行分析,默认值为true。 resolveExternals表示在分析xml时,外部定义或document type definition(DTD)等是否被处理,MSXML6.0中的默认值为false。

另外要解释一下VARIANT类型,一般在COM中用的比较多。VARIANT类型被用来表示多种数据类型,在接口中应用还是很方便的。其实它的定义是一个结构体,其中有一个变量指示了数据的真正类型,还有一个union变量,由各种类型的数据成员构成。这样,VARIANT就能支持各种类型的数据了。值得一提的是,VARIANT中字符串类型是用BSTR表示的,BSTR也是COM编程中通用的字符串类型,为Unicode字符串。BSTR字符串的内存分配都由系统统一管理,通过SysAllocString和SysFreeString控制。Windows提供了专门的类来处理VARIANT和BSTR,具体可以参考这篇文章:http://www.vckbase.com/document/viewdoc/?id=1096。 load函数既可以加载本地文件,也可以加载URL形式的远程文件(没有测试)。另外还有一个对应的loadXML函数可以直接加载字符串形式的xml,但只支持UTF-16和UCS-2两种编码。

保存xml文件的代码为: try {

if( FAILED( pXmlDoc->save(L\"myData.xml\"))) {

printf(\"Fail reason: %s\\n\ } else {

// success } }

catch(_com_error errorObject) {

printf(\"Exception, HRESULT = 0x%08x\ }

二、获取root节点指针

有了IXMLDOMDocument接口指针,就能很方便的得到root节点接口指针。对于加载xml来说,有3种方式,代码如下:

MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->documentElement; 或

MSXML2::IXMLDOMElementPtr pRootNode; pXmlDoc->get_documentElement(&pRootNode); 或

MSXML2::IXMLDOMNodePtr pRootNode, pNode; pXmlDoc->get_firstChild(&pRootNode);

while( pRootNode) {

MSXML2::DOMNodeType type; pRootNode->get_nodeType(&type); if(type==NODE_ELEMENT) break;

pNode = pRootNode;

pNode->get_nextSibling(&pRootNode); }

最常用的又简单的方法就是第一种。写出后两种方法是想说明两个问题,后面的操作方法将只介绍最常用的方法。

可以看到第二种方法并不是直接访问的IXMLDOMDocument接口的属性值,而是通过函数得到。对于DOM接口的属性,一般都有对应的get或put函数来对属性进行读写。

第三种方法是为了让大家再次理解各种类型的node之间的联系与区别,我们可以看到IXMLDOMDocument和IXMLDOMElement均为一个IXMLDOMNode,我们可以通过遍历IXMLDOMDocument的子节点得到root节点。只不过要注意的是,IXMLDOMDocument的get_firstChild返回的节点并不一定就是root,可能是一些注释或空格行之类,我们需要判断节点类型。节点类型的种类及说明如下表:

种类 值 意义

子节点类型 父节点类型 NODE_ELEMENT 1

表示一个元素

ProcessingInstruction, Text, Comment, CDATASection, EntityReference, Element Document, DocumentFragment, EntityReference, Element NODE_ATTRIBUTE 2

表示元素的属性

Text , EntityReference

-

NODE_TEXT 3

表示一个标签的文本 -

Attribute, DocumentFragment, Element, EntityReference NODE_CDATA_SECTION 4

表示一个CDATA section -

DocumentFragment, EntityReference, Element NODE_ENTITY_REFERENCE 5

表示实体引用

Element, Text, ProcessingInstruction, Comment, CDATASection, EntityReference Attribute, DocumentFragment, Element, EntityReference NODE_ENTITY 6

表示扩展实体

可表示该实体的节点类型 DocumentType

NODE_PROCESSING_INSTRUCTION 7

表示一个操作指示 -

Document, DocumentFragment, Element, EntityReference NODE_COMMENT 8

表示注释 -

Document, DocumentFragment, Element, EntityReference NODE_DOCUMENT 9

表示xml文档

Element, ProcessingInstruction, Comment, DocumentType -

NODE_DOCUMENT_TYPE 10

表示文档类型声明,出现在标签中 Notation, Entity Document

NODE_DOCUMENT_FRAGMENT 11

表示文档片段或与文档

Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference -

NODE_NOTATION 12

表示DTD中声明的表示法 -

Document

而对于新建的一个xml来说,我们创建IXMLDOMDocument接口后,调用createElement_x函数创建的第一个节点即为root节点。 MSXML应用总结开发篇(下) 三、查询XML文档节点

这部分属于\"读\"XML文档并做节点遍历,由于担心加上实例会占用过多的篇幅影响阅读,先在这篇做方法总结,以后有时间再写一篇\"实战篇\"专门写个实例工程,可以有更完整的参考代码。

查询和遍历XML文档的大致步骤:创建IXMLDOMDocument接口对象 -> load加载文档 ->得到root节点 ->依次遍历各节点。也可以通过IXMLDOMDocument接口的selectSingleNode或selectNodes函数分别得到指定节点或节点集合。 1、查询文档中指定节点

MSXML2::IXMLDOMNodePtr pNode = pXmlDoc->selectSingleNode(L\"root/record\"); if( pRootNode == NULL) {

// fail process }

selectSingleNode函数允许用类似路径的XPath方式查询节点,返回第一个符合的节点。 2、查询节点集合

MSXML2::IXMLDOMNodeListPtr pNodeList = pXmlDoc->selectNodes(L\"root/record \"); if( pNodeList == NULL) {

// fail process }

与上面方法不同的是,selectNodes函数返回的是一个节点接口指针列表。需要说明的是,这两个函数是IXMLDOMNode接口的函数,因此可以从任一节点进行这样的查询,使用相对调用节点的相对路径即可。如果通过节点的标签名来查询,也可以使用getElementsByTagName函数,该函数不如selectNodes功能丰富,但使用起来比较简单。在IXMLDOMNode和IXMLDOMElement接口中均实现了该函数。

MSXML2::IXMLDOMNodeListPtr pNodeList = pXmlDoc->getElementsByTagName_r(\"tag name\");

if( pNodeList == NULL) {

// fail process }

int nCount = pNodeList->Getlength(); pNodeList->reset();

for( int i=0; iMSXML2::IXMLDOMNodePtr pNode = pNodeList->Getitem(i); if(pNode) {

// node process } }

3、查询节点属性

查询IXMLDOMElement接口节点的某个属性值:

_variant_t varValue = pRootNode->getAttribute(\"attirbute name\"); if( varValue.vt != VT_NULL) printf(\"%s\

或者可以先得到IXMLDOMAttribtute接口,通过接口函数查询属性值:

MSXML2::IXMLDOMAttributePtr pAttriNode = pRootNode->getAttributeNode(\"attirbute name\");

if( pAttriNode) {

_variant_t varValue;

HRESULT hr = pAttriNode->get_nodeval_rue(&varValue); if( SUCCEEDED(hr)) {

printf(\"%s\ } }

IXMLDOMNode接口类中有attributes成员变量,可以直接拿到节点属性的集合,再通过IXMLDOMNamedNodeMap接口查询属性值:

MSXML2::IXMLDOMNamedNodeMapPtr pAttrs = pRootNode->Getattributes(); if( pAttrs) {

MSXML2::IXMLDOMNodePtr pNode = pAttrs->getNamedItem(\"attirbute name\"); if( pNode) {

_variant_t varValue;

HRESULT hr = pNode->get_nodeval_rue(&varValue); if( SUCCEEDED(hr))

printf(\"%s\ } }

也可以通过IXMLDOMNamedNodeMap的元素遍历来查询。 4、查询节点内容

从IXMLDOMNode继承的接口都可以直接查询节点内容: _bstr_t bstrText = pNode->Gettext(); printf(\"%s\

若节点类型是CDATA SECTION,则Gettext函数返回的是CDATA的文本内容;若为Comment

类型则返回注释内容。 5、查询节点名称

对于元素类型节点或者属性节点,有时需要查询其标签名或者属性名,可以用IXMLDOMNode接口函数:

_bstr_t bstrName = pNode->GetnodeName(); printf(\"%s\

注意GetnodeName函数对于不同类型的节点得到的名称种类是不同的,具体可参考MSDN。 四、创建或修改XML文档节点

这部分属于\"写\"XML文档,大致的步骤是:创建IXMLDOMDocument接口对象 ->创建root节点并添加到document上 ->依次创建所需类型的节点并添加到父节点。对于修改已有XML文档节点,只需要按照上面查询节点的方法找到该节点,用get相对应的put函数修改即可。下面主要介绍一下创建的详细过程。 1、创建节点

下面是document添加root节点的代码:

MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->createElement_x(\"root\"); pXmlDoc->appendChild(pRootNode);

一般情况下,创建节点的步骤都是由IXMLDOMDocument接口对象create一个类型节点出来,然后由父节点接口对象调用appendChild函数将创建节点添加上去。总结一下创建各类型节点接口的方法:

IXMLDOMAttribute :createAttribute

IXMLDOMCDATASection :createCDATASection IXMLDOMComment :createComment

IXMLDOMDocumentFragment :createDocumentFragment IXMLDOMElement :createElement_x IXMLDOMEntityReference :createEntityReference IXMLDOMProcessingInstruction :createProcessingInstruction IXMLDOMText :createTextNode 另外还有一个createNode函数可以创建指定类型的节点。 2、设置创建节点各种类型值 下面是设置一个节点的内容代码:

MSXML2::IXMLDOMNodePtr pNode = pXmlDoc->createElement_x(\"title\"); if( pNode) {

pNode->Puttext(\"title text\");

pRootNode->appendChild(pNode); }

只需要调用各类型接口对应的put函数进行设置就可以了。 3、设置创建节点的属性

两种方法,一种是先添加IXMLDOMElement类型节点再设置属性: _variant_t varLanguage = \"chinese\";

HRESULT hr = pRootNode->setAttribute(\"language\ ASSERT(SUCCEEDED(hr));

另一种是直接添加IXMLDOMAttribute类型节点:

MSXML2::IXMLDOMAttributePtr pAttribute = pXmlDoc->createAttribute(\"language\");

if(pAttribute) {

_variant_t varLanguage = \"chinese\"; pAttribute->Putvalue(varLanguage);

pRootNode->setAttributeNode(pAttribute); }

4、插入节点

插入节点可以用insertBefore函数,代码如下:

MSXML2::IXMLDOMElementPtr pNewElement = pXmlDoc->createElement_x(\"date\"); if( pNewElement) {

HRESULT hr = pRootNode->insertBefore(pNewElement, (_variant_t)pRootNode->GetchildNodes()->Getitem(1)); ASSERT(SUCCEEDED(hr)); }

对于不同类型的节点,此函数要求插入的节点类型和返回值类型都有比较复杂的规范,具体可以参考MSDN,在此不详细介绍了。 5、删除节点

对于不同类型的节点接口,有不同的remove函数可以删除节点,总结如下: IXMLDOMElement : removeAttribute,removeAtrributeNode IXMLDOMNamedNodeMap : removeNamedItem IXMLDOMAttribute,IXMLDOMComment,IXMLDOMDocument,IXMLDOMDocumentFragment,IXMLDOMElement,IXMLDOMNode,IXMLDOMText : removeChild

示例代码:

MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->documentElement; pRootNode->removeAttribute(\"languge\");

这篇就总结这些,希望对大家有所帮助;如果有写的不对之处,请不吝赐教 1 / 3

因篇幅问题不能全部显示,请点此查看更多更全内容