MSXM简单的使用
// xml.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <string>
#include <atlconv.h>
using namespace std;
#import "C:\\Windows\\System32\\msxml6.dll"
int _tmain(int argc, _TCHAR* argv[])
{
MSXML2::IXMLDOMDocumentPtr xmlPtr = NULL;
MSXML2::IXMLDOMElementPtr xmlRoot = NULL;
MSXML2::IXMLDOMNodeListPtr nodeList;
MSXML2::IXMLDOMElementPtr childNode;
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr))return 0;
hr = xmlPtr.CreateInstance(__uuidof(MSXML2::DOMDocument));
if (FAILED(hr))return 0;
xmlPtr->put_async(VARIANT_FALSE);
_variant_t szFilePath = _T("1454.xml");
VARIANT_BOOL ret = xmlPtr->load(szFilePath);
if (ret)
{ xmlRoot = xmlPtr->GetdocumentElement();
wprintf_s(_T("%s\r\n"), xmlRoot->GetnodeName().copy());
childNode = xmlRoot->selectSingleNode(_T("//output"));
childNode->get_childNodes(&nodeList);
LONG iCount = NULL;
iCount = nodeList->Getlength();
for (LONG i = 0; i < iCount; i++)
{
childNode = nodeList->Getitem(i);
USES_CONVERSION;
printf_s("%s ----%s \r\n", W2A(childNode->nodeName.GetBSTR()), W2A(childNode->GetnodeTypedValue().bstrVal));
}
}
childNode = NULL;
nodeList = NULL;
xmlRoot = NULL;
xmlPtr = NULL;
::CoUninitialize();
getchar();
return 0;
}
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文档的大致步骤:创建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
IXMLDOMAttribute,IXMLDOMComment,IXMLDOMDocument,IXMLDOMDocumentFragment,IXMLDOMElement,IXMLDOMNode,IXMLDOMText : removeChild
示例代码:
MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->documentElement;
pRootNode->removeAttribute("languge");
MSXM简单的使用的更多相关文章
- 【造轮子】打造一个简单的万能Excel读写工具
大家工作或者平时是不是经常遇到要读写一些简单格式的Excel? shit!~很蛋疼,因为之前吹牛,就搞了个这东西,还算是挺实用,和大家分享下. 厌烦了每次搞简单类型的Excel读写?不怕~来,喜欢流式 ...
- Fabio 安装和简单使用
Fabio(Go 语言):https://github.com/eBay/fabio Fabio 是一个快速.现代.zero-conf 负载均衡 HTTP(S) 路由器,用于部署 Consul 管理的 ...
- node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理
一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...
- 哪种缓存效果高?开源一个简单的缓存组件j2cache
背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...
- 在Openfire上弄一个简单的推送系统
推送系统 说是推送系统有点大,其实就是一个消息广播功能吧.作用其实也就是由服务端接收到消息然后推送到订阅的客户端. 思路 对于推送最关键的是服务端向客户端发送数据,客户端向服务端订阅自己想要的消息.这 ...
- 我的MYSQL学习心得(一) 简单语法
我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...
- 使用 Nodejs 搭建简单的Web服务器
使用Nodejs搭建Web服务器是学习Node.js比较全面的入门教程,因为要完成一个简单的Web服务器,你需要学习Nodejs中几个比较重要的模块,比如:http协议模块.文件系统.url解析模块. ...
- ASP.NET Aries 入门开发教程2:配置出一个简单的列表页面
前言: 朋友们都期待我稳定地工作,但创业公司若要躺下,也非意念可控. 若人生注定了风雨飘摇,那就雨中前行了. 最机开始看聊新的工作机会,欢迎推荐,创业公司也可! 同时,趁着自由时间,抓紧把这系列教程给 ...
- 简单入门canvas - 通过刮奖效果来学习
一 .前言 一直在做PC端的前端开发,从互联网到行业软件.最近发现移动端已经成为前端必备技能了,真是不能停止学习.HTML5新增的一些东西,canvas是用的比较多也比较复杂的一个,简单的入门了一下, ...
随机推荐
- 知识点补充,set集合,深浅copy
一:对之前知识点的补充 1;字符串(str)中的join方法.把列表转换成字符串 2;列表list[ ]和字典dic{ }在循环过程中不能字节删除.需要把要删除的内容记录在新列表中.然后在循环新列表, ...
- @雅礼集训01/06 - T3@ math
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给出 n, m, x,你需要求出下列式子的值: \[\sum_{ ...
- day1_python之字符串的常用操作
python字符串操作常用操作,如字符串的替换.删除.截取.复制.连接.比较.查找.分割等,需要的朋友可以参考下. 1.去除空格 str.strip():删除字符串两边的指定字符,括号的写入指定字符, ...
- phpstorm鼠标显示问题
https://segmentfault.com/q/1010000004319802 使用phpstorm,不知道碰到了什么键,鼠标变成了一个字符那么宽的灰色色块,原来是一根很细的竖线,怎么弄?没法 ...
- 2002年NOIP普及组复赛题解
题目涉及算法: 级数求和:入门题: 选数:搜索: 产生数:搜索.高精度: 过河卒:动态规划. 级数求和 题目链接:https://www.luogu.org/problemnew/show/P1035 ...
- CentOS7.0下安装FTP服务的方法
http://www.jb51.net/article/106604.htm 本篇文章主要介绍了CentOS7.0下安装FTP服务的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟 ...
- js(一) 三大事件 实现注册验证
ps:小声比比,为什么一周多没更,因为js真的好难啊. 上一周做了一整周的jsp+sevlet+mysql做了一个MVC模式的最基本的新闻系统源码会有空搞出来的 好累 好多的. 三大事件 (鼠标事件. ...
- navicat primium 12免安装版的解决方式
https://blog.csdn.net/wanghailong_qd/article/details/85887825 工具————选项 -----环境 会发现是instantclient_10_ ...
- H3C 多路径网络中环路产生过程(2)
- 2018-2-13-win10-uwp-InkCanvas控件数据绑定
title author date CreateTime categories win10 uwp InkCanvas控件数据绑定 lindexi 2018-2-13 17:23:3 +0800 20 ...