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 <msxml3.dll> 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", (LPCSTR)pXmlDoc->parseError->Getreason());

}

else

{

// success

}

}

catch(_com_error errorObject)

{

printf("Exception, HRESULT = 0x%08x", errorObject.Error());

}

上面代码中,开始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", (LPCSTR)pXmlDoc->parseError->Getreason());

}

else

{

// success

}

}

catch(_com_error errorObject)

{

printf("Exception, HRESULT = 0x%08x", errorObject.Error());

}

二、获取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

表示文档类型声明,出现在<!DOCTYPE>标签中

Notation,  Entity

Document

NODE_DOCUMENT_FRAGMENT

11

表示文档片段或与文档

Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference

NODE_NOTATION

12

表示DTD中声明的表示法

Document

而对于新建的一个xml来说,我们创建IXMLDOMDocument接口后,调用createElement_x函数创建的第一个节点即为root节点。

三、查询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; i<nCount; i++)

{

MSXML2::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", _bstr_t(varValue));

或者可以先得到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", _bstr_t(varValue));

}

}

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", _bstr_t(varValue));

}

}

也可以通过IXMLDOMNamedNodeMap的元素遍历来查询。

4、查询节点内容

从IXMLDOMNode继承的接口都可以直接查询节点内容:

_bstr_t bstrText = pNode->Gettext();

printf("%s", bstrText);

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

5、查询节点名称

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

_bstr_t bstrName = pNode->GetnodeName();

printf("%s", bstrName);

注意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", varLanguage);

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

IXMLDOMAttributeIXMLDOMCommentIXMLDOMDocumentIXMLDOMDocumentFragmentIXMLDOMElementIXMLDOMNodeIXMLDOMText  :
removeChild

示例代码:

MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->documentElement;

pRootNode->removeAttribute("languge");

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

MSXML应用总结的更多相关文章

  1. C++ 操作XML文件 使用MSXML.DLL

    使用MSXML.DLL读写XML; 文件顶部加入 #import "msxml3.dll"; using namespace MSXML2; //这两句作用是,在程序的文件夹下生成 ...

  2. MSXML使用教程

    在DOM接口规范中,有四个基本的接口:Document,Node,NodeList以及NamedNodeMap.在这四个基本接口中,Document接口是对文档进行操作的入口,它是从Node接口继承过 ...

  3. MsXml创建和解析XML示例

    一.MsXml创建XML文档示例 // XmlCreationDemo.cpp #include <stdlib.h> #include <stdio.h> // 引入MSXM ...

  4. msxml 操作xml

    1.简介 在.NET平台,微软为C#或托管C++程序员提供了丰富的类库,用以支持各种需求,其中就有对XML文件操作的丰富的类.例如XMLDocument, XmlElement等.但是C++标准库中并 ...

  5. MSXML insertBefore(IXMLDOMNode *newChild, VARIANT refChild) 传参

    在xml操作中经常会用到在某一个节点后或前面插入一个节点,MSXML DOM 中使用的函数是insertBefore(IXMLDOMNode *newChild, VARIANT refChild): ...

  6. 史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南-C++

    目录 史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南 流程设计 xml信息有哪几种读取形式(xml文件或wchar) 如何选取节点,and取节点属性有哪些方法? IXMLDOMNode与I ...

  7. Office2010安装需要MSXML版本6.10.1129.0的方法

    今天给朋友装Office2010,由于朋友之前使用的是绿化版的0ffice2007,所以卸载后安装Office遇到了若要安装Office2010,需要在计算机上安装MSXML版本6.10.1129.0 ...

  8. office2010安装需MSXML版本6.10.1129.0详解解

    https://blog.csdn.net/qq_40824474/article/details/82390606 office2010安装提示报错 由于下列原因,安装程序无法继续,需要计算机安装M ...

  9. 【转载】MSXML应用总结 概念篇

    原文:http://blog.sina.com.cn/s/blog_48f93b530100e9tr.html 微软提供了大量的XML开发工具和技术,而SMXML(Microsoft XML Core ...

随机推荐

  1. string和char*的相互转换

    原文地址: 点击打开链接

  2. 基于css3的轮播效果

    花了一上午来调整页面在ie10上的显示问题,sass编译生成的css文件在ie内核下一直不能正确加载,果然兼容性的问题还需要好好研究.转入正题,用css3实现轮播效果主要是基于css3的framewo ...

  3. PKU1008

    题名:玛雅历 题意:历法转换 . 代码: // 1008.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iost ...

  4. SP Flash Tool使用异常集锦

    1.The load scatter file is invalid无法载入scatter文件 (ubuntu下)我如果我们在使用MTK的Smart Phone Flash Tool过程中无法载入Sc ...

  5. JAVA 语法基础综合练习——学生成绩管理系统

    代码如下:package com.lovo.manager; import java.util.Scanner; /** * 学生管理 * * @author Administrator * */ p ...

  6. sublime 3 安装配置

    Sublime Text 3安装与使用   本文是Sublime Text 全程指引 by Lucida (http://www.cnblogs.com/figure9/p/sublime-text- ...

  7. codeforces 724D(贪心)

    题目链接:http://codeforces.com/contest/724/problem/D 题意:给定一个字符串和一个数字m,选取一个一个子序列s,使得对于字符串中任意长度为m的子序列都至少含有 ...

  8. CGAL4.1在VS2010上配置

    配这个环境花了好几天的时间,虽然网上有很多相关的步骤,但是还是出了不少小错误,具体的步骤有很多,我就只记下我遇到的问题,我用的是CGAL4.1 boost1.51 CMake2.8 Qt4.8.2: ...

  9. javascript常用数组算法总结

    1.数组去重 方法1: JavaScript //利用数组的indexOf方法 function unique (arr) { var result = []; for (var i = 0; i & ...

  10. 一次 surface pro 3 的售后保修 黑色三月维权(HSD)

    已更新结束....原创 半根毛线 博文原址 http://www.cnblogs.com/hsd-/ 发现大量转载 转载请与作者联系 drizzle1996@outlook.com或注明转载 ---- ...