VC++中操作XML(MFC、SDK)XML在Win32程序方面应该没有在Web方面应用得多,很多Win32程序也只是用XML来存存配置信息而已,而且没有足够的好处的话还不如用ini。
VC++里操作XML有两个库可以用:MSXML和XmlLite。
MSXML又细分了两种接口:DOM和SAX2。
XP没自带有XmlLite,只自带有2.x、3.x版的MSXML,不支持SAX2(需要MSXML 4.0以上),所以优先使用DOM。
DOM是以COM形式提供的,VC++里调用DOM可以分3种方法:1、MFC里用CComPtr调用2、SDK里直接调用DOM接口3、SDK里用智能指针调用这3种方法本质上是一样的,区别只不过在于需要编码的多少而已,用CComPtr可以极大的简化代码,下面是几个例子。
例子stocks.xml:<?xml version="1.0"encoding="utf-8"?><root><node1>text1</node1><node2><childnode1attrib1="value1"attrib2="value2"/><childnode2attrib1="value1"attrib2="value2">childtext1</childnode2></node2></root>这个例子应该包含了XML最常见的特征了吧?MFCMFC里可以直接使用DOM,不需要手动添加额外的头文件,只需要在CWinApp::InitInstance()里调用CoInitialize(NULL)初始化COM,在CWinApp::ExitInstance里调用CoUninitialize()释放COM就行了。
//读取XMLCComPtr<IXMLDOMDocument> spDoc;//DOMspDoc.CoCreateInstance(CLSID_DOMDocument);VARIANT_BOOL vb;spDoc->load(CComVariant(OLESTR("stocks.xml")), &vb);//加载XML文件CComPtr<IXMLDOMElement> spRootEle;spDoc->get_documentElement(&spRootEle);//根节点CComPtr<IXMLDOMNodeList> spNodeList;spRootEle->get_childNodes(&spNodeList);//子节点列表long nLen;spNodeList->get_length(&nLen);//子节点数for(long i =0; i != nLen;++i)//遍历子节点{CComPtr<IXMLDOMNode> spNode;spNodeList->get_item(i, &spNode);ProcessNode(spNode);//节点处理函数}//写入XMLCComPtr<IXMLDOMNode> spNode;spRootEle->selectSingleNode(OLESTR("/root/node1"), &spNode);spNode->put_text(OLESTR("newText"));//写入textspRootEle->selectSingleNode(OLESTR("/root/node2/childnode1/@attrib1"), &spNode); spNode->put_nodeValue(CComVariant(OLESTR("newValue")));//写入valueCComPtr<IXMLDOMNode> spNewNode;spDoc->createNode(CComVariant(NODE_ELEMENT), OLESTR("childnode3"), OLESTR(""), &spNewNode);//创建新节点spRootEle->selectSingleNode(OLESTR("/root/node2"), &spNode);spNode->appendChild(spNewNode, &spNewNode);//将新节点加为node2的子节点spNewNode->put_text(OLESTR("childtext2"));//写入新节点textCComQIPtr<IXMLDOMElement> spEle = spNewNode;//注意这里使用CComQIPtrspEle->setAttribute(OLESTR("attrib1"), CComVariant(OLESTR("value1")));//给新节点添加属性spDoc->save(CComVariant(OLESTR("stocks.xml")));//节点处理函数void ProcessNode(CComPtr<IXMLDOMNode>& spNode){CComBSTR bsNodeName;spNode->get_nodeName(&bsNodeName);//节点名AfxMessageBox(COLE2CT(bsNodeName));CComVariant varVal;spNode->get_nodeValue(&varVal);//节点值AfxMessageBox(COLE2CT(varVal.bstrVal));DOMNodeType eNodeType;spNode->get_nodeType(&eNodeType);if(eNodeType == NODE_ELEMENT)//只有NODE_ELEMENT类型才能包含有属性和子节点{//递归遍历节点属性CComPtr<IXMLDOMNamedNodeMap> spNameNodeMap;spNode->get_attributes(&spNameNodeMap);long nLength;spNameNodeMap->get_length(&nLength);for(long i =0; i != nLength;++i){CComPtr<IXMLDOMNode> spNodeAttrib;//注意属性也是一个IXMLDOMNode spNameNodeMap->get_item(i, &spNodeAttrib);ProcessNode(spNodeAttrib);}//递归遍历子节点CComPtr<IXMLDOMNodeList> spNodeList;spNode->get_childNodes(&spNodeList);spNodeList->get_length(&nLength);for(long i =0; i != nLength;++i){CComPtr<IXMLDOMNode> spChildNode;spNodeList->get_item(i, &spChildNode);ProcessNode(spChildNode);}}}对于<tag>text</tag>这样的节点,get_nodeValue会得到空,要得到"text"的话可以遍历子节点(只有一个子节点,它的nodeName为"#text",nodeType为NODE_TEXT,nodeValue就是"text");也可以用get_text 直接得到"text",但是对于这样的节点<tag>text<childtag>childtext</childtag></tag>,get_text会同时得到"text"和"childtext",不过这样的节点应该是不允许的。
DOM里使用的字符串(BSTR)都是OLESTR类型,默认情况下OLESTR是Unicode字符,MFC里可以用COLE2CT把LPCOLESTR转换为LPCTSTR。
对于自己定义的XML,大多数时候不需要遍历,可以通过调用selectNodes、selectSingleNode指定XPath 直接读取某个节点或属性:CComPtr<IXMLDOMDocument> spDoc;//DOMspDoc.CoCreateInstance(CLSID_DOMDocument);VARIANT_BOOL vb;spDoc->load(CComVariant(OLESTR("stocks.xml")), &vb);//加载XML文件CComPtr<IXMLDOMElement> spRootEle;spDoc->get_documentElement(&spRootEle);//根节点CComPtr<IXMLDOMNodeList> spNodeList;CComPtr<IXMLDOMNode> spNode;spRootEle->selectNodes(OLESTR("/root/node2/*"), &spNodeList);//得到node2下的所有子节点spRootEle->selectSingleNode(OLESTR("/root/node2/childnode1/@attrib1"), &spNode);//得到childnode1的attrib1属性XPath的语法可以参考XML文档或MSDN。