日期:2008-07-21  浏览次数:20451 次

写Unmanaged Code在.NET时代成为一种很悲惨的事,当你需要处理XML文件时,这种感觉会变得尤其强烈。FCL中的System.XML多简单啊,连Steve Ballmer都知道怎么用。


事情不会总是那么理想的,如果你要在C/C++程序里处理XML怎么办呢?

选择一:市面上的XML lib还是有几个的,最有名的当然是libXML。我一年前用过,很不错,我还特意写了一份简明教程,后来不知搁哪儿了。

选择二:MS的MSXML,我要介绍的就是这个。

先说一下在MSDN哪里找文档吧,往下看的时候也好有个参考:在Index里打:Windows Media Services 9 Series SDK=>Programming Reference=>Programming Reference (C++)=>XML DOM Interfaces (C++)。什么?Windows Media?呵呵,不错,我觉得这个guide反而是最清楚的,你直接找MSXML,得到的结果,我觉得还没这个好。

在C程序里调用MSXML基本就是一堆COM接口,不过在Visual Studio里操作先要做点简单的设置:

在你的Project里Add References=>COM标签=>Microsoft XML v4.0,5.0其实也有了,但因为是和office一起发布的,觉得有点怪,不想用,反正也未必用什么很怪异的功能,4.0可以了。



然后在加入这两行:

#include <msXML2.h>
#import <msXML4.dll>

头文件和dll库。什么?在哪里加?头文件或者c/cpp文件啊,哪里合适放哪儿。

然后就开始编程了,先定义两个必用的变量:

IXMLDOMDocumentPtr XMLFile = NULL;
IXMLDOMElement* XMLRoot = NULL;

为什么是必用的?  汗...

第一步当然是初始化COM:

if(FAILED(CoInitialize(NULL))) ....

接下来初始化XMLFile对象:

if(FAILED(XMLFile.CreateInstance("MsXML2.DOMDocument.4.0"))) ...

然后就可以加载XML文件了:

_variant_t varXML(L"C:\\test.XML"); //L for unicode
VARIANT_BOOL varOut;
XMLFile->load(varXML, &varOut);

取得root element:

XMLFile->get_documentElement(&XMLRoot))

取得第一级element:

IXMLDOMNodeList* XMLChildNodes = NULL;
XMLRoot->get_childNodes(&XMLChildNodes);

遍历所有第一级element:

IXMLDOMNode* currentNode = NULL;
while(!FAILED(XMLChildNodes->nextNode(&currentNode)) && currentNode != NULL)
{
//do something
}

取得当前element的名称:

BSTR nodeName;
currentNode->get_nodeName(&nodeName);

取得当前element的一个attribute(假设这个attribute叫type)的值:

IXMLDOMNamedNodeMap* attributes = NULL;
IXMLDOMNode* attributeName = NULL;
_bstr_t bstrAttributeName = "type";
BSTR nameVal;
currentNode->get_attributes(&attributes);
attributes->getNamedItem(bstrAttributeName, &attributeName);
attributeName->get_text(&nameVal);

需要注意的是,你要记住释放所有的借口,IXMLDOM***->Release(),这可不是.NET,有人帮你GC,你得自己调用Release()来减reference count,it's COM, remember?

好了,大致就这样,顺便提一下XPath:

_bstr_t bstrXMLQuery = L"/books/book[@type=scifi and @author=fox]";
IXMLDOMNodeList* nodes = NULL;
if(FAILED(XMLRoot->selectNodes(bstrXMLQuery, &nodes)) || FAILED(nodes->get_length(&length)) || length == 0)
//no match found or something went wrong
else
//match found

上面是找这样的node:

<books>
<book type="scifi" author="fox">....
</book>
....
</books>

具体的XPath语法就查手册吧,到处都有。

哦,对了,忘了说:如果你全部用ATL的类的话,借口的调用会简单一点,不过很容易转换的,比如:

IXMLDOMDocument* 对应 IXMLDOMDocumentPtr(我这里用了),其他基本也是加个Ptr,我不废话了。

最后提供一个sample,我临时攒的。工作的时候写的程序当然不能拿来贴的,呵呵。这个sample基本就是遍历整个XML,然后报告一遍文件的结构,对每个node,如果它有一个叫id的attribute,就同时打印id的值。If you want the complete VS project, shoot me an email. But I guess no one really needs it anyway, right, : )

#include "stdafx.h"
#include <windows.h>
#include <msXML2.h>
#import <msXML4.dll>

HANDLE logFile = NULL;

#define INDENT 4

#define TESTHR(hr)
{
if(FAILED(hr)) goto fail;
}

void PrintChild(IXMLDOMNodeList* nodeList, int level)
{
if(nodeList == NULL)
return;

IXMLDOMNode* currentNode = NULL;
IXMLDOMNodeList* childNodes = NULL;
IXMLDOMNamedNodeMap* attributes = NULL;
IXMLDOMNo