王朝网络
分享
 
 
 

MSXML的DOM模型处理XML

王朝other·作者佚名  2006-02-04
宽屏版  字体: |||超大  

简单介绍:通过MSXML类库的使用理解XML文件处理模型中基于文档对象模型(DOM)的处理

使用MSXML对XML文件进行处理较早就已经接触到了,也写过一篇叫“XML文件的处理思考”的随笔,不经意用google搜索一下这篇文章,发现竟然被很多网站转载,版权就不用提,看到不汗颜就已经不错。其实也不是特意厚颜去google一下,在mblogger.cn的博客可以查看链接,发觉这个文章的搜索比较多来自google。到了现在,回头去看看这篇文章,冷汗直冒啊!在说明里面的缺陷以前还是先花点时间先把DOM的主要结构说一下,方便后面的理解。如果不了解怎么开始使用MSXML,看看刚才提到的这篇文章还是有点用的,地址:http://ms.mblogger.cn/ohahu/posts/4563.aspx

DOM模型在MSXML类库中的主要表现为把XML文件倒入内存,形成一个IXMLDOMDocument,再把其中的每一个部件都用一个接口对应起来。因为还没用MSXML进行过XSLT格式化XML文件,所以这相关的也只好避而不谈了。先来个有XML基本部件的文本:

(1)<?xml version='1.0' encoding='GB2312'?>

(2)<?xml-stylesheet type='text/xsl' href='/expert/Xsl/2.xsl'?>

(3)<body>

(4) <code1 id=”text”>文本</code>

(5) <code2 id=”cdata”>

(6) <![CDATA[

(7) 这里是CDATA的内容,可以放类似’<’等可能会和XML控制信息有冲突的内容

(8) ]]>

(9) </code>

(10)</body>

为了表述方便,在第一列都放上了行号。首先从(1)~(10)够成了一个Document,在MSXML中对应IXMLDOMDocument接口;(1)行、(2)行对应MSXML中的接口为IXMLDOMProcessingInstruction;(3)行到(10)行则为一个Root Element,对应的接口为IXMLDOMElement,(3)里面包含的多个<code>也是element,不过是body的下一层element。需要注意的是Root只能有一个,Root下面无论多深,理论上允许无数多个(?),且允许名称重复;(4)和(5)里面各有一个id=”?”这是一个attribute(注意:(1)和(2)行version;encoding;type;href等也是),对应MSXML里的IXMLDOMAttribute;(4)里面的“文本”看起来和(7)这一行是差不多的,都是文本信息,很多人以为都可以通过get_text()直接得到(不久以前我也以为),其实是错的。对于“文本”是可以通过(4)这个element直接get_text()获取,对应于IXMLDOMText,但如果在(5)这个element直接get_text()就会出错,原因?CDATA是区别于“文本”的另外一种类型,对应于IXMLDOMCDATASection,如何获取,后面再提。现在整个XML的基本框架似乎出来了:

<IXMLDOMDocument>

<IXMLDOMProcessingInstruction />

<IXMLDOMProcessingInstruction />

<IXMLDOMElement (根,只能有一个)>

<IXMLDOMElement IXMLDOMAttribute>

IXMLDOMText

</ IXMLDOMElement >

<IXMLDOMElement IXMLDOMAttribute>

IXMLDOMCDATASection

</ IXMLDOMElement >

<IXMLDOMElement>

</IXMLDOMDocument>

但是,看看很多关于MSXML的教程用到另外一个接口IXMLDOMNode,这个怎么回事,和上面的IXMLDOMElement有什么关系。前面用了这么长时间的MSXML经常就是在这个地方弄混,以至无所进展,最近要在C++Builder5下面使用XML解析,可是没有TXMLDocument控件,想自己封装一下MSXML的一些使用才发现其中的奥妙。在DOM模型中把包括Document、ProcessingInstruction、Attribute、Element、TextNode、CDATASection等都看作是一个个Node,在MSXML中实现接口的时候表现为这些对象都是从Node派生出来的,要理解Node和这一些接口的关系关键是要看清楚各接口之间的派生关系。为什么要添加这一个Node接口呢?目的是为了使XML各要素之间联结起来,不至于松散。

再加上另外两个接口IXMLDOMNodeList和IXMLDOMNamedNodeMap。其中IXMLDOMNamedNodeMap主要使用在联结同一个Element的各Attribute,因为同一个Element的Attribute必须保证两两不能冲突,而IXMLDOMNodeList则用于联结其他的几个要素ProcessingInstruction、Element、TextNode、CDATASection等,而这几个要素比如Element,在同一个层是允许相同名称重复出现的。上面的联结并没有包括Document这个Node,因为Document是整个XML的第一个Node,不可能也不允许出现在IXMLDOMNodeList和IXMLDOMNamedNodeMap下面。这段说法我也算是经过代码证实过的吧!把MSXML的Document作为第一个Node,然后通过NamedNodeMap编历该层的所有Attribute,再通过NodeList递归循环下一层的所有Node。可以看到这样的一个树状结构(其中的attribute在该行括号列出,缩进表示所在层):

#document

xml (version;encoding)

xml-stylesheet (type; href)

body

code1 (id)

#text

code2 (id)

#cdata-section

从上面打印出来的信息可以看出TextNode,CDATASection是当作Element的下一层Node来处理的。get_text()只是为了方便使用TextNode的一个捷径,对CDATASection通过get_text()访问会出错,则可考虑通过下一层Node的第一个Node来获取。方法:

MSXML::IXMLDOMNodePtr child;

child = parent->childNodes->get_item(0);

if (child != NULL)

text = child->get_nodeValue();

在递归循环的时候,通常我们并不能预知这一个Node是什么类型的。为了知道这一个Node是Element还是TextNode,或者其他类型,可以通过IXMLDOMNode::nodeType来获取,这是一个枚举类型,有如下取值(从这也可以看出,上面这个XML文本并没有涵盖XML所有要素):

NODE_ELEMENT (1)

NODE_ATTRIBUTE (2)

NODE_TEXT (3)

NODE_CDATA_SECTION (4)

NODE_ENTITY_REFERENCE (5)

NODE_ENTITY (6)

NODE_PROCESSING_INSTRUCTION (7)

NODE_COMMENT (8)

NODE_DOCUMENT (9)

NODE_DOCUMENT_TYPE (10)

NODE_DOCUMENT_FRAGMENT (11)

NODE_NOTATION (12)

好了,下面应该可以把“XML文件的处理思考”里面出现的一些问题一个个罗列出来了吧:

问题一:最大的问题,通过路径来找到比较深入的一个节点。

在这篇文章中通过递归调用函数来实现这个功能,完全没有必要,算是多此一举了。DOM模型中自身带的IXMLDOMNode::SelectSingleNode和IXMLDOMNode::SelectNodes(XPath)实现了比这个递归调用更完美的功能。简单说一下SelectSingleNode的使用方法(msdom是IXMLDOMDocument实例)

MSXML::IXMLDOMNodePtr parent, child;

parent = msdom->documentElement;

//child为code1类型Element

child = parent->selectSingleNode(“/code1”);

//child为code1的id类型Attribute

child = parent->selectSingleNode(“/code1@id”);

//child名为code1且Attribute id=’text’的那个Element类型Element

child = parent->selectSingleNode(“/code1@[id=’text’]”);

…其它高深点的用法待后研究,为XPath相关。

参考地址:http://sqq876.blogchina.com/2486119.html

问题二:C++ Builder6里面的TXMLDocument并不单纯是对MSXML的封装

为了在C++ Builder 5下面封装出一个类似的控件来,找了一些相关资料,发现MSXML、OpenXML等DOM模型的解析器都是同一套接口(希望我没有弄错),只是内部实现不同。TXMLDocument通过设置Ventor可以设置使用不同的解析器,但是在C++Builder里面使用方法却是完全相同的。默认好像是使用MSXML解析,比较优劣,MSXML需在客户端注册较新的msxml.dll类库;SAX的需要附带较大的dll;OpenXML因为是直接使用一个.pas文件编译,直接生成到了可执行文件。

问题三:在文章中遍历NodeList使用了IEnum接口

有点杀鸡用牛刀之嫌,现在的遍历可以这样:

for (int i = 0; i < nodelist->get_length(); i++)

{

child = nodelist->get_item((long)i);

name = child->get_nodeName();

}

想想原来做的时候应该也用过这个方法,不过当时不知道Node和Element之间的关系,胡乱执行下面这样的转换所以转换出的element == NUL,就以为此路不通。

IXMLDOMElementPtr element = (IXMLDOMNodePtr)node;

问题三:到后来补的一段appendChild的操作,将createElement出来的Element直接转换成Node再appendChild。其实,这应该是最基础的一个C++知识,关键是要看清MSXML实现的Com里面也带有了C++的这种技巧。

现在再来看看C++ Builder 5里面怎么解决没有TXMLDocument控件,要使用MSXML类库有什么办法。

最开始想到的是使用import语句的方法,即

#import "C:\\Windows\\system32\\MSXML.DLL" named_guids

可是,import进来能生成tlb和tlh文件,进行编译却无法通过,总提示缺少了些什么,或有一些函数未能导出来(对了,可以在VC++里面import,然后复制生成的tlb和tlh到C++Builder项目)。于是,直接使用C++ Builder里面自带的TVariant类进行COM实例化,调用函数,属性(OleFunction,OlePropertyGet,OlePropertySet)等。例:

TVariant varMSDOM = CreateOleObject(“MSXML.DOMDocument”);

varMSDOM.OleFunction(L“load”, L”c:\\tmp.xml”);

TVariant varDoc = varMSDOM.OlePropertyGet(“documentElement”);

直觉是这种调用方法是会比import进来的调用速度要慢。差别好像是import直接通过虚函数表查找函数指针进行调用;OleFunction这些则通过IDispatch接口的invoke函数,间接调用,而且不能调用import进来name_guids的那些函数,如get_nodeName(BSTR *)。不管怎么样,通过TVariant还是基本能够满足要求,函数的调用。

然后,突然发现C++ Builder的Project上有个Import From Type Library的菜单,也尝试一下。tlb和tlh文件都生成了,加入工程编译一下,能够通过,不过tlh里面的定义,和调用方法却和VC里面的import进来有些不同:

1. VC里面的实例化直接用智能指针如

MSXML::IXMLDOMDocumentPtr msdom;

msdom.CreateInstance(__uuidof(MSXML::DOMDocument));

C++Builder里面的实例化,则通过另外一个编译器封装的对象来实现

TCOMIXMLDOMDocument i_xmldocument = CoDOMDocument::Create();

IXMLDOMDocumentPtr msdom = (IXMLDOMDocumentPtr) i_xmldocument;

TCOMIXMLDOMDocument的定义在MSXML2_TLB.h里面可以找到

typedef TComInterface<IXMLDOMDocument> TCOMIXMLDOMDocument;

2. C++Builder里面也有IXMLDOMDocumentPtr msdom,可是这个指针却不能直接用于判断是否等于NULL,编译器会提示错误,而应改为

if ((IXMLDOMDocument *)msdom == NULL)

上面在C++Builder下面使用MSXML的经验,延伸到其它类型的COM的自动化(automation),应该是不会有什么问题的!不是吗?

hahu原作:

http://ohahu.mblogger.cn

http://blog.csdn.net/hahu

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有