使用MSXML进行基本的XML操作
最近做了一个XML相关的功能,这里总结一下使用MSXML进行XML操作时使用到的基本方法. 对任何文件的操作无非就是"读"和"写". 由于XML的树形存储结构,使得在处理一些结构化数据时非常好用,比如保存配置或者记录数据等.之所以选择使用MSXML,是因为本着尽可能不引入第三方库的想法. 既然MSXML唾手可得,那么拿来用也无妨. 一. 读操作 首先是导入MSXML,我使用的是MSXML4.dll #import<msxml4.dll> 因为MSXML的很多操作是与组件相关的,所以要初始化COM. ::CoInitialize(NULL); //..... ::CoUninitialize(); 好了,接下来就是打开一个文件. MSXML2::IXMLDOMDocumentPtr pDoc; HRESULT hr = pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument40)); if (FAILED(hr)) { return 0; } if (!pDoc->load("C:1.xml")) { pDoc.Release(); return 0; }
打开文件之后就可以进行读取操作了. 由于XML的结构中既有节点,又有属性,所以我们分两种情况来处理.
1. 读取节点 节点的处理比较简单.首先我们考虑最简单的情况,如XML文件内容为: <root> <data>12345</data> <data>67890</data> </root>如果只是读取第一个data的数据,那么: MSXML2::IXMLDOMElementPtr pRoot = pDoc->GetdocumentElement(); // Get root node MSXML2::IXMLDOMNodePtr pNode = pRoot->GetfirstChild(); VARIANT varVal; pNode->get_nodeTypedValue(&varVal); cout << "node name: " << (char*)pNode->nodeName << "," << "node value: " << (char*)(_bstr_t)varVal << endl; 如果有许多子节点的话,那么可以获取一个list,然后再从list中取出每个节点. MSXML2::IXMLDOMNodeListPtr pNodeList = pRoot->GetchildNodes(); assert(pNodeList != NULL); int nCount = (int)pNodeList->length; for (int i = 0; i < nCount; i++) { pNode = pNodeList->item[i]; pNode->get_nodeTypedValue(&varVal); cout << "node name: " << (char*)pNode->nodeName << "," << "node value: " << (char*)(_bstr_t)varVal << endl; }除了直接取list中的item之外,还可以使用属性get_item,如: pNodeList->get_item(0,&pNode);在MSXML中的很多操作都可以使用属性或者是调用对应的方法实现,虽然操作不同,但是结果是一致的,如上面所述的GetchildNodes就是方法,对应的属性操作就是get_childNodes. 这里就不一一列举了.
2. 读取属性 读取属性的方法有多种,既可以通过枚举将每个属性读出,也可以通过指定属性名,取出想要的属性. 我比较倾向于后一种. 如有XML文件内容如下: <root> <data attr1="123" attr2="345" attr3="456"/> </root>现在我们想读取属性"attr1"的值,操作如下: MSXML2::IXMLDOMNodePtr pAttr = pNode->Getattributes()->getNamedItem("attr1"); pAttr->get_nodeValue(&varVal);如果读者足够细心的话,可能会注意到这个属性get_nodeValue,它和前面使用的get_nodeTypedValue有什么区别呢? MSXML2::IXMLDOMNodePtr pAttr = pNode->Getattributes()->getNamedItem("attr1"); pAttr->get_nodeValue(&varVal); 实际上,如果我们再读取节点内容的时候,如果使用get_nodeValue根本就不会读到任何内容. 对于上文提到的pNode和pAttr,我们可以使用get_nodeTypeString操作下便可知道两者的区别: VARIANT varType; pNode->get_nodeTypeString(&varType); pAttr->get_nodeTypeString(&varType);这两个节点的类型是不同的,至于有什么不同,就请查阅MSDN吧.^_^
好啦,那么如果我们想取出节点的每个属性该如何操作呢? 其实和遍历节点的操作是类似的: MSXML2::IXMLDOMNodePtr pAttr; MSXML2::IXMLDOMNamedNodeMapPtr pNodeMap = pNode->Getattributes(); int nCount = pNodeMap->length; for (int i = 0; i < nCount; i++) { pAttr = pNodeMap->item[i]; pAttr->get_nodeValue(&varVal); } 二. 写操作 要进行写入,首先要有一个文档的指针: MSXML2::IXMLDOMDocumentPtr pDoc; HRESULT hr = pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument40)); if (FAILED(hr)) { return 0; } 那么同样,我们针对节点和属性做不同的处理. 1. 写入节点 同读节点一样,也非常简单,例如我们要写这样的内容: <root> <data>123123</data> </root>直接创建节点然后写入数据: MSXML2::IXMLDOMElementPtr pRoot; pRoot = pDoc->createElement((_bstr_t)"root"); pDoc->appendChild(pRoot); MSXML2::IXMLDOMElementPtr pSon; pSon = pDoc->createElement((_bstr_t)"data"); pSon->Puttext((_bstr_t)"123123"); pRoot->appendChild(pSon); 2. 写入属性 现在我们要为节点添加属性,如果要创建的xml文件内容如下: <root> <data attr1="123" attr2="345"/> </root>相应的代码片段如下: MSXML2::IXMLDOMElementPtr pRoot; pRoot = pDoc->createElement((_bstr_t)"root"); pDoc->appendChild(pRoot); MSXML2::IXMLDOMElementPtr pSon; pSon = pDoc->createElement((_bstr_t)"data"); VARIANT varVal; CString str1("123"); CString str2("456"); varVal.vt = VT_BSTR; varVal.bstrVal = (_bstr_t)str1; pSon->setAttribute("attr1",varVal); varVal.bstrVal = (_bstr_t)str2; pSon->setAttribute("attr2",varVal); pRoot->appendChild(pSon); HRESULT nhr = pDoc->save("C:11.xml"); 3. 保存文件 HRESULT nhr = pDoc->save("C:1.xml");以上对于XML的基本操作,也是比较常用的操作. 对于平时使用的添加,删除节点,或者通过递归实现遍历整个文件,都是通过这些基本操作实现的. 看起来似乎有点麻烦,其实使用多了也就还好啦. 需要注意的是,因为MSXML中的函数使用了组件技术,所以在变量的声明周期结束时要记得释放,遇到CComBSTR,BSTR和VARIANT时尤其小心,不要造成泄漏. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |