|
<script language="javascript" src="/ad/js/edu_left_300-300.js" type="text/javascript"></script> | 写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可以了。
然后在加入这两行:
#i nclude <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(¤tNode)) && 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, : )
#i nclude "stdafx.h" #i nclude <windows.h> #i nclude <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; IXMLDOMNode* attributeID = NULL;
while(!FAILED(nodeList->nextNode(¤tNode)) && currentNode != NULL) { BSTR nodeName; TESTHR(currentNode->get_nodeName(&nodeName)); DWORD dwBytesWritten; for(int i=0; i<level*INDENT; i++) WriteFile(logFile, L" ", (DWORD)(sizeof(WCHAR)), &dwBytesWritten, NULL);
//WCHAR msg[MAX_SIZE]; //wsprintf(msg, L"%s ", nodeName); WriteFile(logFile, nodeName, (DWORD)(wcslen(nodeName)*sizeof(WCHAR)), &dwBytesWritten, NULL);
TESTHR(currentNode->get_attributes(&attributes)); if(attributes!=NULL) { _bstr_t bstrAttributeName = "id"; BSTR idVal; TESTHR(attributes->getNamedItem(bstrAttributeName, &attributeID)); if(attributeID != NULL) { TESTHR(attributeID->get_text(&idVal)); WriteFile(logFile, L" ", (DWORD)(sizeof(WCHAR)), &dwBytesWritten, NULL); WriteFile(logFile, idVal, (DWORD)(wcslen(idVal)*sizeof(WCHAR)), &dwBytesWritten, NULL); WriteFile(logFile, L"/r/n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL); attributeID->Release(); attributeID = NULL; } else { WriteFile(logFile, L"/r/n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL); } attributes->Release(); attributes = NULL;
} else { WriteFile(logFile, L"/r/n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL); }
TESTHR(currentNode->get_childNodes(&childNodes)); PrintChild(childNodes, level+1); currentNode=NULL; }
fail: if(childNodes!=NULL) childNodes->Release(); if(attributeID!=NULL) attributeID->Release(); if(attributes!=NULL) attributes->Release(); if(currentNode != NULL) currentNode->Release(); }
int _tmain(int argc, _TCHAR* argv[]) {
IXMLDOMDocumentPtr xmlFile = NULL; IXMLDOMElement* xmlRoot = NULL; _variant_t varXml(L"C://demo1.xml");
logFile = CreateFile(L"log.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(logFile == INVALID_HANDLE_VALUE) goto fail;
TESTHR(CoInitialize(NULL));
TESTHR(xmlFile.CreateInstance("Msxml2.DOMDocument.4.0"));
VARIANT_BOOL varOut; TESTHR(xmlFile->load(varXml, &varOut));
TESTHR(xmlFile->get_documentElement(&xmlRoot));
BSTR rootName; DWORD dwBytesWritten; TESTHR(xmlRoot->get_nodeName(&rootName)); WriteFile(logFile, rootName, (DWORD)(wcslen(rootName)*sizeof(WCHAR)), &dwBytesWritten, NULL); WriteFile(logFile, L"/r/n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL);
IXMLDOMNodeList* xmlChildNodes = NULL; TESTHR(xmlRoot->get_childNodes(&xmlChildNodes));
PrintChild(xmlChildNodes, 2);
fail: if(logFile != INVALID_HANDLE_VALUE) CloseHandle(logFile); if(xmlChildNodes!=NULL) xmlChildNodes->Release(); if(xmlRoot!=NULL) xmlRoot->Release(); return 0; }
| |