最近作C++相關的項目,遇到同時使用COM和MSXML來解析XML文件中信息的問題,這類問題若是作MFC開發也會常常用到。
在網上搜了一整圈,確實很難找到可用的code,總算本身研究出高效而簡單的方法,藉此機會總結一下,並分享給你們。node
附 VS Project鏡像:
SimpleParser4MSXML-cpp: C++語言寫的MSXML的簡單使用示例, COM 和 MFC 開發中比較經常使用。
https://github.com/yanglr/Sim...
點擊」Raw」可看到源碼,歡迎fork或star~ ios
首先簡要列舉一下MSXML技術的基本特色。git
基於 COM 的技術,用於處理 Windows 操做系統隨附的 XML。 | |
---|---|
MSXML | 提供 DOM 本機實現,同時支持 XPath 和 XSLT。 |
包含 SAX2 基於事件的分析器。 |
首先簡要介紹一下大概流程:github
須要解決的問題:windows
從文件中導入xml內容,使用url或filePath數組
VARIANT_BOOL bSuccess = false; HRESULT hr = iXMLDoc->load(CComVariant(L"./test.xml"), &bSuccess); // 此處的L能夠省略
當已變量方式傳人filePath時,須要使用c_str()函數轉換一下,代碼以下:app
VARIANT_BOOL bSuccess = false; filePath = "./test.xml"; HRESULT hr = iXMLDoc->load(CComVariant(filePath.c_str()), &bSuccess);
先定義一個BSTR常量ide
const wchar_t *src = L"" L"<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n" L"<root desc=\"Great\">\r\n" L" <text>Hey</text>\r\n" L" <layouts>\r\n" L" <lay index=\"15\" bold=\"true\"/>\r\n" L" <layoff index=\"12\"/>\r\n" L" <layin index=\"17\"/>\r\n" L" </layouts>\r\n" L"</root>\r\n";
而後從BSTR導入xml內容:函數
VARIANT_BOOL bSuccess = false; iXMLDoc->loadXML(CComBSTR(src), &bSuccess);
注: BSTR字符串是用於COM組件對象模型的字符串格式, 字符串以表示字符串長度的4字節整數開始, 而後跟上UTF-16編碼的wchar_t字符串(包括0結束標誌)。BSTR類型的變量是一個指針, 指向字符串的第一個字符處。
ui
CComBSTR sstrRoot(L"root"); // sstrRoot("root"); CComPtr<IXMLDOMNode> rootNode; HRESULT hr = iXMLDoc->selectSingleNode(sstrRoot, &rootNode); CComPtr<IXMLDOMNode> textNode; hr = rootNode->selectSingleNode(CComBSTR(L"text"), &textNode); // 搜索第一個"text"節點
IXMLDOMElement接口繼承於IXMLDOMNode接口,但除了從IXMLDOMNode接口繼承的方法以外,IXMLDOMElement接口還向外暴露如下方法:
方法 | 說明 |
---|---|
get_tagName | 檢索元素名稱(在tag之間的文本)。 |
getAttribute | 檢索所指定名字的屬性的值。 |
getAttributeNode | 檢索所指定名字的屬性的節點 |
getElementsByTagName | 檢索與提供的名稱匹配的全部子元素的列表。 |
removeAttribute | 移動或替換給定名稱的屬性 |
removeAttributeNode | 從這個元素中移除指定的屬性 |
setAttribute | 爲給定名稱的屬性設置值 |
setAttributeNode | 在此元素上添加或替換提供的屬性節點。 |
先使用get_childNodes函數得到子節點列表,而後遍歷之用get_item依次取出每一項進行處理。
CComPtr<IXMLDOMElement> pRootElement; CComPtr<IXMLDOMNodeList> pNodeList; pRootElement->get_childNodes(&pNodeList); // Child node list long nLen; pNodeList->get_length(&nLen); // Child node list for (long index = 0; i != nLen; ++index) // Traverse { CComPtr<IXMLDOMNode> pCurNode; hr = pNodeList->get_item(index, &pCurNode); do(); // 此處可作任何你想作的事情 }
使用Element->setAttribute()便可,具體以下:
CComPtr<IXMLDOMElement> imageElement; xmlDocData->createElement(CComBSTR(L"Image"), &imageElement); // 建立節點"Image" imageElement->setAttribute(CComBSTR(L"Type"), CComVariant(CComBSTR(imageType.c_str()))); // 添加屬性"Type"
CComBSTR ssName; printf("Node name:%ls\n", ssName); // 用%ls打印BSTR字符串內容 SysFreeString(ssName); // 用完字符串後必須釋放
或
CComBSTR ssName; wprintf(L"Node name:%s\n", ssName); // 這裏的L不能省略 SysFreeString(ssName);
將CComBSTR類字符串的內容複製到wstring中,而後使用wcout輸出
CComBSTR ssName; wstring bstrText(ssName); wcout << bstrText << endl;
對CStringW類字符串而言,這已是一種比較簡單的方式了。
CComBSTR ssName; CString cstring(ssName); wcout << (LPCTSTR)cstring << endl;
CComBSTR ssName; CW2A printstr(ssName); cout << printstr << endl;
#include <msxml6.h> // 含有 MSXML最新版 #include <atlbase.h> #include "atlstr.h" // 含有CString, CStringW和CW2A #include <iostream> // 包含wcout函數 #include <string> // 包含 c_str()函數, wcout #include "comutil.h" // 包含_bstr_t using namespace std; const wchar_t *src = L"" L"<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n" L"<root desc=\"Great\">\r\n" L" <text>Hey</text>\r\n" L" <layouts>\r\n" L" <lay index=\"15\" bold=\"true\"/>\r\n" L" <layoff index=\"12\"/>\r\n" L" <layin index=\"17\"/>\r\n" L" </layouts>\r\n" L"</root>\r\n"; int main() { CoInitialize(NULL); // Initialize COM CComPtr<IXMLDOMDocument> iXMLDoc; // Or use CComPtr<IXMLDOMDocument2>, CComPtr<IXMLDOMDocument3> try { HRESULT hr = iXMLDoc.CoCreateInstance(__uuidof(DOMDocument)); // iXMLDoc.CoCreateInstance(__uuidof(DOMDocument60)); // Load the file. VARIANT_BOOL bSuccess = false; // Load it from a url/filename... hr = iXMLDoc->load(CComVariant(L"./test.xml"), &bSuccess); // filePath = "./test.xml"; // hr = iXMLDoc->load(CComVariant(filePath.c_str()), &bSuccess); // or from a BSTR... // iXMLDoc->loadXML(CComBSTR(src), &bSuccess); // Get a smart pointer (sp) to the root CComPtr<IXMLDOMElement> pRootElement; hr = iXMLDoc->get_documentElement(&pRootElement); // Root elements // Get Attribute value of the note "root" CComBSTR ssDesc("desc"); CComVariant deVal(VT_EMPTY); hr = pRootElement->getAttribute(ssDesc, &deVal); CComBSTR sstrRoot(L"root"); // sstrRoot("root"); CComPtr<IXMLDOMNode> rootNode; hr = iXMLDoc->selectSingleNode(sstrRoot, &rootNode); // Search "root" CComBSTR rootText; hr = rootNode->get_text(&rootText); if (SUCCEEDED(hr)) { wstring bstrText(rootText); wcout << "Text of root: " << bstrText << endl; } CComPtr<IXMLDOMNode> descAttribute; hr = rootNode->selectSingleNode(CComBSTR("@desc"), &descAttribute); // Atrribute須要用@, 而各個節點不能使用@做爲前綴來搜索 CComBSTR descVal; hr = descAttribute->get_text(&descVal); if (SUCCEEDED(hr)) { wstring bstrText(descVal); wcout << "Desc Attribute: " << bstrText << endl; } if (!FAILED(hr)) { wstring strVal; if (deVal.vt == VT_BSTR) strVal = deVal.bstrVal; wcout << "desc: " << strVal << endl; } CComPtr<IXMLDOMNodeList> pNodeList; pRootElement->get_childNodes(&pNodeList); // Child node list long nLen; pNodeList->get_length(&nLen); // Child node list for (long i = 0; i != nLen; ++i) // Traverse { CComPtr<IXMLDOMNode> pNode; hr = pNodeList->get_item(i, &pNode); CComBSTR ssName; CComVariant val(VT_EMPTY); hr = pNode->get_nodeName(&ssName); if (SUCCEEDED(hr)) { wstring bstrText(ssName); wcout << "Name of node " << (i + 1) << ": " << bstrText << endl; CString cstring(ssName); // To display a CStringW correctly, use wcout and cast cstring to (LPCTSTR), an easier way to display wide character strings. wcout << (LPCTSTR)cstring << endl; // CW2A converts the string in ccombstr to a multi-byte string in printstr, used for display output. CW2A printstr(ssName); cout << printstr << endl; } } /// Add(Append) node CComPtr<IXMLDOMDocument>& xmlDocData(iXMLDoc); CComPtr<IXMLDOMElement> imageElement; CComPtr<IXMLDOMNode> newImageNode; string imageType = "jpeg"; char buffer[MAX_PATH]; GetCurrentDirectory(MAX_PATH, buffer); // Get Current Directory string path(buffer); // Copy content of char*, generate a string string imagePath = path + "\\com.jpg"; xmlDocData->createElement(CComBSTR(L"Image"), &imageElement); imageElement->setAttribute(CComBSTR(L"Type"), CComVariant(CComBSTR(imageType.c_str()))); // 爲當前節點添加屬性 imageElement->setAttribute(CComBSTR(L"FileName"), CComVariant(CComBSTR(imagePath.c_str()))); rootNode->appendChild(imageElement, &newImageNode); /// Remove "text" node under "root" node CComPtr<IXMLDOMNode> xmlOldNode; CComPtr<IXMLDOMNode> textNode; hr = rootNode->selectSingleNode(CComBSTR(L"text"), &textNode); // Search "text" node hr = rootNode->removeChild(textNode, &xmlOldNode); /// Update XML hr = iXMLDoc->save(CComVariant("updated.xml")); } catch (char* pStrErr) { // Some error... std::cout << pStrErr << std::endl << std::endl; } // catch catch (...) { // Unknown error... std::cout << "Unknown error..." << std::endl << std::endl; } // Release() - that gets done automatically, also can manually do for each opened node or elements. // iXMLDoc.Release(); // Stop COM CoUninitialize(); system("pause"); return 0; }
運行結果:
運行完,獲得的update.xml內容爲:
https://raw.githubusercontent...
參考資料:
本文原載於本人csdn博客 →
https://blog.csdn.net/lzuacm/...