msxml dom 全解析
2016-03-12 07:50
225 查看
msxml 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
print?
#import <msxml4.dll> raw_interfaces_only
using namespace MSXML2;
#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当然也不例外, 如下:
[cpp]
view plain
copy
print?
CoInitialize(NULL); .... ....//msxml相关的操作在这里 CoUninitialize();
CoInitialize(NULL); .... ....//msxml相关的操作在这里 CoUninitialize();
2 创建对象
[cpp]
view plain
copy
print?
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"); }
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
[cpp]
view plain
copy
print?
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(); }
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文档的内容如下:
[html]
view plain
copy
print?
<?xml version="1.0" encoding="utf-8"?> <book> <name>Fly</name> <price discount = "80%">23.5</price> </book>
<?xml version="1.0" encoding="utf-8"?> <book> <name>Fly</name> <price discount = "80%">23.5</price> </book>
代码:
[cpp]
view plain
copy
print?
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%"
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
[cpp]
view plain
copy
print?
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);
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所做的修改,仅限于内存,并没有保存到文件里.
相关文章推荐
- 客户端网页编程,第二章思维导图
- C语言回顾与再学习——数组与指针
- C++ Exceptional 写异常安全的代码
- 客户端网页编程,第一章思维导图
- Linux/Unix工具与正则表达式的POSIX规范
- 为什么要对url进行encode
- lintcode-medium-4 Sum
- java多线程与线程间通信的原理和方法
- [leetcode] 170. Two Sum III - Data structure design 解题报告
- Unix Network Programming1
- Mycat 月分片方法 - pursuer.chen - 博客园
- python代码风格-PEP8
- lintcode-medium-3 Sum Closest
- Rain
- ASUS TR-N14U固件中继小白教程
- L18函數:靈活即強大
- json-schema-core(liuhailong翻译)
- lintcode-medium-3 Sum
- [leetcode] 288. Unique Word Abbreviation 解题报告
- 获取、增加、修改、删除sqlserver字段描述及快速查看表字段与描述