msxml dom 全解析


作者:小马


原文链接:http://blog.csdn.net/pony_maggie/article/details/6706422

 

从题目你应该获取到以下的信息:
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都是存在的, 所以直接以<>的形式包含就可以了.
动态链接
只要把下面两行加到代码里

[cpp]  view plain copy
  1. #import <msxml4.dll> raw_interfaces_only  
  2. using namespace MSXML2;  


 

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


三 msxml DOM基本操作


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


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

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

[cpp]  view plain copy
  1. CoInitialize(NULL);  
  2. ....  
  3. ....//msxml相关的操作在这里  
  4.   
  5. CoUninitialize();  


 

2 创建对象

[cpp]  view plain copy
  1. MSXML2::IXMLDOMDocument *pxmldoc = NULL;  
  2. HRESULT hr;  
  3.   
  4. hr = CoCreateInstance(__uuidof(DOMDocument40),  
  5.   NULL,  
  6.   CLSCTX_INPROC_SERVER,  
  7.   __uuidof(MSXML2::IXMLDOMDocument),  
  8.   (void**)&pxmldoc);  
  9.     
  10. if (FAILED(hr))   
  11. {  
  12.     printf("Failed to CoCreate an instance of an XML DOM\n");  
  13. }  

说明
你可能觉得奇怪, 既然前已经声明了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

[cpp]  view plain copy
  1. VARIANT var;  
  2. VARIANT_BOOL status;  
  3. VariantInit(&var);  
  4.   
  5. V_BSTR(&var) = SysAllocString(L"test.xml");  
  6. V_VT(&var) = VT_BSTR;  
  7. pXMLDom->load(var, &status);  
  8. if (status!=VARIANT_TRUE)  
  9.  {  
  10.     printf("Failed to load xml\n");  
  11.     if (&var) VariantClear(&var);   
  12.     if (pXMLDom) pXMLDom->Release();   
  13. }  

说明
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文档的内容如下:

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <book>  
  3.   <name>Fly</name>   
  4.   <price discount = "80%">23.5</price>   
  5. </book>  


代码:

[cpp]  view plain copy
  1. IXMLDOMNode *pNode=NULL;  
  2. BSTR bstr = NULL;  
  3. IXMLDOMElement *pIXMLDOMElement = NULL;  
  4.   
  5. if (bstr) SysFreeString(bstr);  
  6. bstr = SysAllocString(L"book");  
  7. BSTR bstrAttributeName = SysAllocString(L"discount");  
  8.   
  9. pXMLDom->selectSingleNode(bstr, &pNode);  
  10.   
  11. if (!pNode)   
  12. {  
  13.    printf("Failed to selectSingleNode\n");  
  14.    if (bstr) SysFreeString(bstr);  
  15.    if (pXMLDom) pXMLDom->Release();  
  16.    return;  
  17.   
  18. }  
  19. ///  
  20. pNode->get_xml(&bstr);  
  21. printf("book.xml:\n%s\n", _com_util::ConvertBSTRToString(bstr));  
  22. //  
  23. if (bstr)   
  24.   SysFreeString(bstr);  
  25. if (pNode != NULL)  
  26. {  
  27.   pNode->Release();  
  28.   pNode = NULL;  
  29. }  
  30. bstr = SysAllocString(L"book/price");  
  31. pxmldoc->selectSingleNode(bstr, &pNode);  
  32.   
  33. if (bstr)   
  34.  SysFreeString(bstr);  
  35. bstr = SysAllocString(L"discount");  
  36. pNode->QueryInterface(__uuidof(MSXML2::IXMLDOMElement), (void **)&pElement);  
  37. 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

[cpp]  view plain copy
  1. VariantClear(&var);  
  2. V_BSTR(&var) = SysAllocString(L"75%");  
  3. V_VT(&var) = VT_BSTR;  
  4. hr = pElement->setAttribute(bstr, var);  
  5.   
  6. VariantClear(&var);  
  7. V_BSTR(&var) = SysAllocString(L"book.xml");  
  8. V_VT(&var) = VT_BSTR;  
  9. 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
本身的一些基本知识的描述,你把这里面的东东理解就行了.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值