zl程序教程

您现在的位置是:首页 >  其它

当前栏目

msxml dom 全解析

解析 dom
2023-09-27 14:28:31 时间
  

转载请注明出处

作者:小马

 

从题目你应该获取到以下的信息:
1 本文不讲xml,只讲msxml
2 msxml又包括很多方面,比如DOM,SOM,XSLT等,本文只讲DOM

好了,现在开始...

 

一 关于msxml DOM


什么是msxml

MSXML 是一款微软的 xml 语言解析器, 如果你不了解COM,知道这引起就可以了, 否则的话,你应该知道, msxml实际上一种com组件,所谓com组件,可以理解成一个独立的功能模块, 客户程序员只需获取到组件对象, 然后调用里面的接口进行操作. 对于组件内部如何实现, 不需要关心. com组件有很多实现形式, msxml以dll的形式实现(比如msxml4.dll), 事实上这种实现形式也比较通用. 对于com,不是本文的重点,不方便说太多(事实上,我也说不了多少,嘿嘿), 如果感兴趣,可以看看潘总的那本<<com原理与应用>>


什么是DOM
中文意为XML 文档对象模型, 它定义访问和操作XML文档的标准方法和属性. DOM 将 XML 文档作为一个树形结构,而树叶被定义为节点.它本身是W3C定义的一个标准. 举个例子,比如标准定义了如下的节点类型:Document,DocumentFragment,ProcessingInstruction等.


什么是msxml DOM
msxml DOM可以说是微软按照DOM的标准规范,实现的一组API, 它是msxml中一部分, 下面这幅图来自msdn,很形象的表达了这种关系:

图1


二搭建环境


本文所有分析, 源码测试都是基于以下环境: windows XP, visual studio2005, msxml4.0.语言是c/c++

对于搭建msxml4.0, 可以下载安装包安装, 也可以手动安装. 安装包的实际上是执行下列操作, 把msxml4.dll  msxml4a.dll  msxml4r.dll三个文件copy
到system32目录下,并用regsvr32注册. 所以你也可以手动执行这些操作.


配置项目环境
vs2005下有两种方式引用msxml4, 静态链接和动态链接.

静态链接
第一步, #include <msxml2.h>
第二步, 在"属性-链接-输入"中加入msxml2.lib.
有一点要说明, 在vc8的安装目录下,MsXml2.Lib, msxml2.h都是存在的, 所以直接以<>的形式包含就可以了.
动态链接
只要把下面两行加到代码里

#import <msxml4.dll> raw_interfaces_only
using namespace MSXML2;


 

关于raw_interfaces_only,简单说明, 它表示使用原始接口,而不是智能指针封装的接口. 因为缺省时import会自动生成符合automation的接口.如果你不想这样,就加上这一句.


三 msxml DOM基本操作


本文只写一些基本的操作,旨在有助于理解和入门, 更多详细的操作和属性的介绍还得查阅msdn.


msxml DOM的有两种操作方式,一种是用原始的接口,一种是用智能指针的方式, 后者用智能指针技术对前者做了一个封装, 可以自动的处理COM对象引用计数以及动态内存管理等方面的内容. 原理上是一致的. 这部分的所有示例代码都是只给出原始接口的操作,并且采动态加载的方式

1 CoInitialize和CoUninitialize
这两个分别是初始化com库和关闭com库的API,所以基于com的应用,都要在操作开始前调用CoInitialize,并在
结束后调用CoUninitialize, MSXML当然也不例外, 如下:

CoInitialize(NULL);
....
....//msxml相关的操作在这里

CoUninitialize();


 

2 创建对象

MSXML2::IXMLDOMDocument *pxmldoc = NULL;
HRESULT hr;

hr = CoCreateInstance(__uuidof(DOMDocument40),
  NULL,
  CLSCTX_INPROC_SERVER,
  __uuidof(MSXML2::IXMLDOMDocument),
  (void**)&pxmldoc);
  
if (FAILED(hr)) 
{
    printf("Failed to CoCreate an instance of an XML DOM\n");
}

说明
你可能觉得奇怪, 既然前已经声明了using namespace MSXML2, 定义pxmldoc变量的时候为什么还要加上MSXML2::,
如果不加MSXML2::,你会发现编译的时候会出现类似"IXMLDOMDocument ambiguous symbol"错误, 这是因为vc8本身已经包含了msxml的定义, 在头文件Msxml.h已经有这些类型的定义了, 这就是定 义冲突. 要解决这个问题,显示的把命名空间的前缀加上去就可以了.


关于IXMLDOMDocument, 是一个文档对象,它指向整个xml文档,想了解详细的内容可以查阅msdn.

 

CoCreateInstance是com里的东东,你只需知道,通过它可以取得文档对象的接口指针就可以了. 有了这个接口指针,才能调用里的成员函数进行各种操作.

HRESULT是COM里用的比较多的数据类型, 一般用做函数的返回值, 对于这种类型最好不要简单的判断hr == 或hr !=, 而是用SUCCEEDED
判断成功,用FAILED判断失败.

 

3 加载xml

VARIANT var;
VARIANT_BOOL status;
VariantInit(&var);

V_BSTR(&var) = SysAllocString(L"test.xml");
V_VT(&var) = VT_BSTR;
pXMLDom->load(var, &status);
if (status!=VARIANT_TRUE)
 {
    printf("Failed to load xml\n");
    if (&var) VariantClear(&var); 
    if (pXMLDom) pXMLDom->Release(); 
}

说明
load函数的定义如下:
HRESULT load(
    VARIANT xmlSource,
    VARIANT_BOOL *isSuccessful);
VARIANT,VARIANT_BOOL都COM里的数据类型, 这是一种为了跨平台操作而定义的类型, 你要知道VARIANT变量在用之前最好先
VariantInit,用完了VariantClear释放.你可能奇怪为什么要释放,哪里有动态内存分配吗? 这里:
V_BSTR(&var) = SysAllocString(L"test.xml");

 

如果load出错, 同时要记得释放pXMLDom,因为前面CoCreateInstance内部实际上已经执行了AddRef操作,你要负责释放(有点晕了吧?)

 

4 读xml
假设xml文档的内容如下:

<?xml version="1.0" encoding="utf-8"?> 
<book>
  <name>Fly</name> 
  <price discount = "80%">23.5</price> 
</book>


代码:

IXMLDOMNode *pNode=NULL;
BSTR bstr = NULL;
IXMLDOMElement *pIXMLDOMElement = NULL;

if (bstr) SysFreeString(bstr);
bstr = SysAllocString(L"book");
BSTR bstrAttributeName = SysAllocString(L"discount");

pXMLDom->selectSingleNode(bstr, &pNode);

if (!pNode) 
{
   printf("Failed to selectSingleNode\n");
   if (bstr) SysFreeString(bstr);
   if (pXMLDom) pXMLDom->Release();
   return;

}
///
pNode->get_xml(&bstr);
printf("book.xml:\n%s\n", _com_util::ConvertBSTRToString(bstr));
//
if (bstr) 
  SysFreeString(bstr);
if (pNode != NULL)
{
  pNode->Release();
  pNode = NULL;
}
bstr = SysAllocString(L"book/price");
pxmldoc->selectSingleNode(bstr, &pNode);

if (bstr) 
 SysFreeString(bstr);
bstr = SysAllocString(L"discount");
pNode->QueryInterface(__uuidof(MSXML2::IXMLDOMElement), (void **)&pElement);
pElement->getAttribute(bstr, &var);//如果成功,var="80%"

说明
上面的代码展示了如下的操作,
1 selectSingleNode获取根结点"book",并调用get_xml输了该结点所有的内容.
2 selectSingleNode获取根结点"book/price"结点, 并调用getAttribute获取该结点中属性"discount"的值.

 

要注意getAttribute是IXMLDOMElement的方法, IXMLDOMNode无法直接调用, IXMLDOMElement是IXMLDOMNode的子类, IXMLDOMElement的指针可以转化成IXMLDOMNode,但是IXMLDOMNode转化成IXMLDOMElement使用就不推荐了. 一般用下面的方法通 IXMLDOMNode获取IXMLDOMElement.

 

pNode->QueryInterface(__uuidof(MSXML2::IXMLDOMElement), (void **)&pElement);

 

5 写xml

VariantClear(&var);
V_BSTR(&var) = SysAllocString(L"75%");
V_VT(&var) = VT_BSTR;
hr = pElement->setAttribute(bstr, var);

VariantClear(&var);
V_BSTR(&var) = SysAllocString(L"book.xml");
V_VT(&var) = VT_BSTR;
hr = pxmldoc->save(var);

说明
把"discount"的值改为"75%", 主要是save函数, 如果不调用save,你对xml所做的修改,仅限于内存,并没有保存到文件里.

 

 


四 附言
最后,对于在网上经常看到的两个问题, 说一下自己的意见.
Q:

msxml DOM的实际应用中,是原始接口好,还是用智能指针接口好?


ans:
建议用智能指针的接口.
一方面, 避免了频繁的处理AddRef, Release, 而且处理不好容易造成内存泄露. 另一方面,从上面的例子也明显感觉到,
智能指针接口的在一些基本的操作上简单很多,比如属性的直接访问.

 

Q:使用msxml DOM, 有没有必要熟练掌握xml?


ans:

我的经验是, 要看你是做哪一块的应用. 比如你是一个c/c++的工程师, 你只需要
用msxml对xml文档进行基本的读写操作, 那么你只需对xml有个大概的了解就可以了,比如你只要知道你要操作的那些东东在xml中是
什么就可以了. 没有必要通读w3c中xml的规范. 事实上,有个简单的办法可以让你清楚你该了解多少, msdn里在讲到DOM之前,有关于xml
本身的一些基本知识的描述,你把这里面的东东理解就行了.