史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南-C++

史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南-C++

目录史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南流程设计xml信息有哪几种读取形式(xml文件或wchar)如何选取节点,and取节点属性有哪些方法?IXMLDOMNode与IXMLDOMElement接口有何联系、区别节点如果是数组,怎么操作?如何为属性插入属性字符串的转换与输出主要代码文末彩蛋

史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南

最近做C++相关的项目,遇到同时使用COM和MSXML来解析XML文件中信息的问题,这类问题如果做MFC开发也会经常用到。

在网上搜了一整圈,确实很难找到可用的code,总算自己研究出高效而简单的方法,借此机会总结一下,并分享给大家。

附 VS Project镜像:

SimpleParser4MSXML-cpp: C++语言写的MSXML的简单使用示例, COM 和 MFC 开发中比较常用。

https://github.com/yanglr/SimpleParser4MSXML-cpp

点击”Raw”可看到源码,欢迎fork或star~

首先简要列举一下MSXML技术的基本特点。

基于 COM 的技术,用于处理 Windows 操作系统随附的 XML。

MSXML

提供 DOM 本机实现,同时支持 XPath 和 XSLT。

包含 SAX2 基于事件的分析器。

流程设计

首先简要介绍一下大概流程:

初始化COM

创建一个IDOMDocument对象xmlDoc,使用xmlDoc -> load() 或 loadXML()方法读入 XML源

调用selectNodes()或者selectSingleNode()函数,选取指定的节点对象。

通过IXMLDOMNode对象的属性和方法读取节点对象的内容。

通过IXMLDOMNode对象的属性和方法设置节点对象的内容。

通过调用xmlDoc -> save()保存XML文件。

关闭COM

需要解决的问题:

xml信息有哪几种读取形式(xml文件或wchar)

如何选取节点,and取节点属性有哪些方法?

IXMLDOMNode 与 IXMLDOMElement 接口有什么联系和区别?

节点如果是数组,怎么操作?

如何为属性插入属性

字符串的转换

xml信息有哪几种读取形式(xml文件或wchar)

xml文件

从文件中导入xml内容,使用url或filePath

VARIANT_BOOL bSuccess = false;

HRESULT hr = iXMLDoc->load(CComVariant(L"./test.xml"), &bSuccess); // 此处的L可以省略

当已变量方式传人filePath时,需要使用c_str()函数转换一下,代码如下:

VARIANT_BOOL bSuccess = false;

filePath = "./test.xml";

HRESULT hr = iXMLDoc->load(CComVariant(filePath.c_str()), &bSuccess);

已以字符串格式读入的xml完整代码

先定义一个BSTR常量

const wchar_t *src = L""

L"\r\n"

L"\r\n"

L" Hey\r\n"

L" \r\n"

L" \r\n"

L" \r\n"

L" \r\n"

L" \r\n"

L"\r\n";

然后从BSTR导入xml内容:

VARIANT_BOOL bSuccess = false;

iXMLDoc->loadXML(CComBSTR(src), &bSuccess);

注: BSTR字符串是用于COM组件对象模型的字符串格式, 字符串以表示字符串长度的4字节整数开始, 然后跟上UTF-16编码的wchar_t字符串(包括\0结束标志)。BSTR类型的变量是一个指针, 指向字符串的第一个字符处。

如何选取节点,and取节点属性有哪些方法?

搜索节点名字

CComBSTR sstrRoot(L"root"); // sstrRoot("root");

CComPtr rootNode;

HRESULT hr = iXMLDoc->selectSingleNode(sstrRoot, &rootNode);

CComPtr textNode;

hr = rootNode->selectSingleNode(CComBSTR(L"text"), &textNode); // 搜索第一个"text"节点

IXMLDOMNode与IXMLDOMElement接口有何联系、区别

IXMLDOMElement接口继承于IXMLDOMNode接口,但除了从IXMLDOMNode接口继承的方法之外,IXMLDOMElement接口还向外暴露以下方法:

方法

说明

get_tagName

检索元素名称(在tag之间的文本)。

getAttribute

检索所指定名字的属性的值。

getAttributeNode

检索所指定名字的属性的节点

getElementsByTagName

检索与提供的名称匹配的所有子元素的列表。

removeAttribute

移动或替换给定名称的属性

removeAttributeNode

从这个元素中移除指定的属性

setAttribute

为给定名称的属性设置值

setAttributeNode

在此元素上添加或替换提供的属性节点。

节点如果是数组,怎么操作?

先使用get_childNodes函数获得子节点列表,然后遍历之用get_item依次取出每一项进行处理。

CComPtr pRootElement;

CComPtr 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 pCurNode;

hr = pNodeList->get_item(index, &pCurNode);

do(); // 此处可做任何你想做的事情

}

如何为属性插入属性

使用Element->setAttribute()即可,具体如下:

CComPtr imageElement;

xmlDocData->createElement(CComBSTR(L"Image"), &imageElement); // 创建节点"Image"

imageElement->setAttribute(CComBSTR(L"Type"), CComVariant(CComBSTR(imageType.c_str()))); // 添加属性"Type"

字符串的转换与输出

直接使用printf函数+“%ls”或wprintf函数+“%s”打印BSTR类字符串

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;

先使用将bstr转为std::wstring,然后wcout

std::wstring wstringName(ssName, SysStringLen(ssName));

wcout << wstringName << endl;

先将CComBSTR类字符串强转为LPCTSTR类型后,然后使用wcout输出

对CStringW类字符串而言,这已经是一种比较简单的方式了。

CComBSTR ssName;

CString cstring(ssName);

wcout << (LPCTSTR)cstring << endl;

将CComBSTR类字符串的内容复制到CW2A类字符串(多字节字符串)中,然后使用wcout输出

CComBSTR ssName;

CW2A printstr(ssName);

cout << printstr << endl;

先使用宏W2A将bstr转为std::string,然后cout

USES_CONVERSION;

std::string stringName = std::string(W2A(ssName));

cout << stringName << endl;

主要代码

#include // 含有 MSXML最新版

#include

#include "atlstr.h" // 含有CString, CStringW和CW2A

#include // 包含wcout函数

#include // 包含 c_str()函数, wcout

#include "comutil.h" // 包含_bstr_t

using namespace std;

const wchar_t *src = L""

L"\r\n"

L"\r\n"

L" Hey\r\n"

L" \r\n"

L" \r\n"

L" \r\n"

L" \r\n"

L" \r\n"

L"\r\n";

int main()

{

CoInitialize(NULL); // Initialize COM

CComPtr iXMLDoc; // Or use CComPtr, CComPtr

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 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 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 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 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 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& xmlDocData(iXMLDoc);

CComPtr imageElement;

CComPtr 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 xmlOldNode;

CComPtr 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.com/yanglr/SimpleParser4MSXML-cpp/master/msxmlDemo/updated.xml

参考资料:

IXMLDOMElement接口

Using the MSXML Parser

MFC C++ XML Parse - Using MSXML

如何:各种字符串类型之间转换 | Microsoft Docs

本文原载于本人的CSDN博客:

史上最最靠谱,又双叒叒(ruò,zhuó)简单的基于MSXML的XML解析指南-C++ - Bravo Yeung-羊较瘦之自留地

作者简介:Bravo Yeung,计算机硕士,知乎干货答主(获81K 赞同, 38K 感谢, 235K 收藏)。曾在国内 Top3互联网视频直播公司工作过,后加入一家外企做软件开发至今。

如需转载,请加微信 iMath7 申请开白!

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

欢迎各位读者加入 .NET技术交流群,在公众号后台回复“加群”或者“学习”即可。

文末彩蛋

微信后台回复“asp”,给你:一份全网最强的ASP.NET学习路线图。

回复“cs”,给你:一整套 C# 和 WPF 学习资源!

回复“core”,给你:2019年dotConf大会上发布的.NET core 3.0学习视频!

相关文章

win10网络重置后,网卡驱动消失且装不上驱动的解决办法
办理网络文化经营许可证要一般多少钱
365bet亚洲官网

办理网络文化经营许可证要一般多少钱

🕒 01-12 👁️ 9637
汉朝君主列表
365bet亚洲官网

汉朝君主列表

🕒 07-26 👁️ 4021
人一般步行一小时,可以走多少公里?
365bet亚洲官网

人一般步行一小时,可以走多少公里?

🕒 10-28 👁️ 488
C语言如何定义字符串?(2种方法)
365比分网APP

C语言如何定义字符串?(2种方法)

🕒 07-22 👁️ 4479
鱼丸煮制攻略:不同种类不同场景的时间掌控法
best365中国官网

鱼丸煮制攻略:不同种类不同场景的时间掌控法

🕒 10-24 👁️ 5273