写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(¤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, : )
#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;
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;
}