您的位置:首页 > 其它

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所做的修改,仅限于内存,并没有保存到文件里.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: