史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南-C++
史上最最靠谱,又双叒叒简单的基于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"<?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类型的变量是一个指针, 指向字符串的第一个字符处。
如何选取节点,and取节点属性有哪些方法?
- 搜索节点名字
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"节点
IXMLDOMNode与IXMLDOMElement接口有何联系、区别
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"
字符串的转换与输出
- 直接使用
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 <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.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学习视频!
史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南-C++的更多相关文章
- Nginx 403 Forbidden 解决方案 史上最靠谱
原因 1. SELinux为开启状态(enabled) 查看SELinux的状态 sestatus 如果不是 disables , 需要 vi /etc/selinux/config 将以前的 SEL ...
- 史上最详细Windows版本搭建安装React Native环境配置 转载,比官网的靠谱亲测可用
史上最详细Windows版本搭建安装React Native环境配置 2016/01/29 | React Native技术文章 | Sky丶清| 95条评论 | 33530 views ...
- Redis分布式锁 (图解-秒懂-史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
- 消息队列面试题、RabbitMQ面试题、Kafka面试题、RocketMQ面试题 (史上最全、持续更新、吐血推荐)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- GitHub上史上最全的Android开源项目分类汇总 (转)
GitHub上史上最全的Android开源项目分类汇总 标签: github android 开源 | 发表时间:2014-11-23 23:00 | 作者:u013149325 分享到: 出处:ht ...
- 史上最全的iOS面试题及答案
迷途的羔羊--专为路痴量身打造的品牌.史上最精准的定位.想迷路都难!闪电更新中...敬请期待,欢迎提意见.下载地址:https://itunes.apple.com/us/app/mi-tu-de-g ...
- 史上最全的 Java 新手问题汇总
史上最全的 Java 新手问题汇总 Java是目前最流行的编程语言之一——它可以用来编写Windows程序或者是Web应用,移动应用,网络程序,消费电子产品,机顶盒设备,它无处不在. 有超过30亿 ...
- [转载]DOS循环:bat/批处理for命令详解 (史上虽详尽的总结和说明~~)
--本文来源于TTT BLOG: http://www.yoyotao.net/ttt/, 原文地址:http://www.yoyotao.net/ttt/post/139.html 前言: 虽然以前 ...
- ArrayList, LinkedList, Vector - dudu:史上最详解
ArrayList, LinkedList, Vector - dudu:史上最详解 我们来比较一下ArrayList, LinkedLIst和Vector它们之间的区别.BZ的JDK版本是1.7.0 ...
随机推荐
- 3d轮播图(另一种方式,可以实现的功能更为强大也更为灵活,简单一句话,比酷狗优酷的炫)
前不久我做了一个3d仿酷狗的轮播图,用的技术原理就是简单的jquery遍历+css样式读写. 这次呢,我们换一种思路(呵呵其实换汤不换药),看到上次那个轮播吗?你有没有发现用jquery的animat ...
- sniffer 软件的使用方法
一.捕获数据包前的准备工作 在默认情况下,sniffer将捕获其接入碰撞域中流经的所有数据包,但在某些场景下,有些数据包可能不是我们所需要的,为了快速定位网络问题所在,有必要对所要捕获的数据包作过滤. ...
- left join 后的条件 位置不同,查询的结果不同
表t_a id name 1 a1 2 a2 表t_b a1_id name num 2 b2 1 3 b3 100 left join 后加查询条件 select a.* from t_a a le ...
- S7-200与SMART 200之间进行数据通讯与监控
S7-200与SMART 200之间进行数据通讯与监控 准备物品:S7-200PLC.SMART200.SCANET模块*2.交换机*1.网线若干. (连接示意图一) 1.在STEP7-MircoWi ...
- SSM-SpringMVC-24:SpringMVC异常高级之自定义异常
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 自定义异常,大家都会,对吧,无非就是继承异常类等操作,很简单,我就不多扯皮了,但是在xml配置文件中有个不同的 ...
- JavaScript设计模式 Item 6 --单例模式Singleton
单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池.全局缓存.浏览器的window对象.在js开发中,单例模式的 ...
- javascript项目实战---ajax实现无刷新分页
分页: limit 偏移量,长度; limit 0,7; 第一页 limit 7,7; 第二页 limit 14,7; 第三页 每页信息条数:7 信息总条数:select count(*) from ...
- Android软键盘弹出,覆盖h5页面输入框问题
之前我们在使用vue进行 h5 表单录入的过程中,遇到了Android软键盘弹出,覆盖 h5页面 输入框 问题,在此进行回顾并分享给大家: 系统:Android 条件:当输入框在可视区底部或者偏下的位 ...
- python3 [爬虫实战] selenium 爬取安居客
我们爬取的网站:https://www.anjuke.com/sy-city.html 获取的内容:包括地区名,地区链接: 安居客详情 一开始直接用requests库进行网站的爬取,会访问不到数据的, ...
- 关于xpath相对路径前加点与不加点的区别
转自:https://blog.csdn.net/qingmu_9923/article/details/51771602 最近在用selenium做web工程自动化测试的相关项目,会经常用到元素定位 ...