文章目录

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

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

最近做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 listlong nLen;pNodeList->get_length(&nLen);    // Child node listfor (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 COMCComPtr<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 rootCComPtr<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 listlong nLen;pNodeList->get_length(&nLen);    // Child node listfor (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) nodeCComPtr<IXMLDOMDocument>& xmlDocData(iXMLDoc);CComPtr<IXMLDOMElement> imageElement;CComPtr<IXMLDOMNode> newImageNode;string imageType = "jpeg";char buffer[MAX_PATH];GetCurrentDirectory(MAX_PATH, buffer);  //  Get Current Directorystring path(buffer); // Copy content of char*, generate a stringstring 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" nodeCComPtr<IXMLDOMNode> xmlOldNode;CComPtr<IXMLDOMNode> textNode;hr = rootNode->selectSingleNode(CComBSTR(L"text"), &textNode); // Search "text" node     hr = rootNode->removeChild(textNode, &xmlOldNode);/// Update XMLhr = iXMLDoc->save(CComVariant("updated.xml"));}catch (char* pStrErr) {// Some error...std::cout << pStrErr << std::endl << std::endl;} // catchcatch (...) {// 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 COMCoUninitialize();system("pause");return 0;
}

运行结果:

运行完,得到的update.xml内容为:
https://raw.githubusercontent.com/yanglr/SimpleParser4MSXML-cpp/master/msxmlDemo/updated.xml

参考资料:

  1. IXMLDOMElement接口
  2. Using the MSXML Parser
  3. MFC C++ XML Parse - Using MSXML
  4. 如何:各种字符串类型之间转换 | Microsoft Docs

史上最最靠谱,又双叒叒(ruò,zhuó)简单的基于MSXML的XML解析指南-C++相关推荐

  1. 【云周刊】第146期:史上最大规模人机协同的双11,12位技术大V揭秘背后黑科技...

    摘要: 史上最大规模人机协同的双11,12位技术大V揭秘背后黑科技,INTERSPEECH 2017系列 | 语音识别之语言模型技术,机器学习初学者必须知道的十大算法,云数据库SQL Server 2 ...

  2. 菜鸟启动“快递包装换鸡蛋”活动 史上最低碳的一届“双11”来了

    11月1日消息,随着11月1日00:00天猫双11第一笔订单付款的完成,快递物流业正式迎来年度高峰. 为助力低碳双11,针对快递包装循环再利用这一绿色物流关键环节,今年双11,菜鸟联合天猫共同推出6万 ...

  3. 史上最靠谱 Ubuntu 镜像下载

    废话不多说,上干货: Ubuntu 14.04: http://mirrors.aliyun.com/ubuntu-releases/14.04/ Ubuntu 16.04: http://mirro ...

  4. hashmap中的key是有序的么_美团面试题:Hashmap结构,1.7和1.8有哪些区别(史上最最详细解析)...

    作者|依本多情 原文:blog.csdn.net/qq_36520235/article/details/82417949 一.真实面试题之:Hashmap的结构,1.7和1.8有哪些区别 不同点: ...

  5. 史上最强鸿蒙教程来袭!跟Android初学者分享几点经验,专题解析

    缘起 经过近十年的发展,Android技术优化日新月异,如今Android 10.0 已经发布,Android系统性能也已经非常流畅,可以在体验上完全媲美iOS.到了各大厂商手里,改源码.自定义系统, ...

  6. 口布杯花的60种叠法_史上最全的口布花折法教程,简单实用!超美呦~

    开这功德金花--这功德金花--" 风信子干笑两声,也知道方元没法糊弄,只能实话实说:"第一点用处,便是能让我们加快融入那个世界,在请外援之时也有意想不到的效果!还有--则是在一些偏 ...

  7. Nginx面试题(史上最全 + 持续更新)

    尼恩面试宝典专题39:Nginx面试题(史上最全.持续更新) 本文版本说明:V27 <尼恩面试宝典>升级规划为: 后续基本上,每一个月,都会发布一次,最新版本,可以联系构师尼恩获取, 发送 ...

  8. ubuntu boot空间不足_windows10安装ubuntu双系统教程(绝对史上最详细)

    windows10安装ubuntu双系统教程(绝对史上最详细) 快车道: Windows10安装ubuntu16.04双系统教程 Windows10安装ubuntu18.04双系统教程 一. 先搞清楚 ...

  9. 史上最难618,TCL夺得电视行业京东和天猫份额双第一

    随着2022年618这场年中大促落下帷幕,我们又迎来了一波晒成绩单的日子. 翻开第三方平台公布的汇总成绩,在今年大盘承压的背景之下,家电行业依旧闪耀.而在家电品类的榜单上,TCL再次登顶. TCL Q ...

最新文章

  1. 是否有可能从python中的句子语料库重新训练word2vec模型(例如GoogleNews-vectors-negative300.bin)?
  2. 《机器人与数字人:基于MATLAB的建模与控制》——2.2节李群和李代数
  3. UWP 推荐 - 限时免费的RSS阅读器《RSS 追踪》登录 Windows 10
  4. 多线程:并发实现方法之J.U.C
  5. matplotlib 一张图多个图,如何在Matplotlib中绘制同一个图上的多个函数?
  6. 传统排插即将淘汰,品胜智能排插率先符合新国标
  7. export function函数传参_04 js高阶函数(惰性函数、柯里化函数、compose函数)和单例设计模式...
  8. 如何使用小程序自定义组件功能
  9. Scality试图将对象存储转移到磁带和云中
  10. 自动化测试——接口测试——增删改查
  11. 目标检测YOLO V5 C++版本
  12. javascript 字符串与数组之间的相互转换(join、split)与splice介绍
  13. 安全测试工具之-Burpsuite
  14. 在3dmax中打开文件时,显示路径无效,如何处理?
  15. php mail cc,邮件cc是什么意思
  16. 数显之家快讯:【SHIO世硕心语】便携显示器可能是智能手机变身电脑最好的配件了!
  17. 论文笔记:Teaching Machine to Read and Comprehend
  18. Python中的算数运算符
  19. 前缀学习完结篇 第三课上
  20. excel2007计算机试题,巧用excel2007/2003进行拍照

热门文章

  1. /动态设置element DatePicker日期组件的disabledDate禁选日期
  2. Exchange报错:452 4.3.1 Insufficient system resources
  3. 键盘事件keydown、keypress、keyup随笔整理总结
  4. Serpent.AI - 游戏代理框架(Python)
  5. 第十三章 外星人来了
  6. python倍数怎么表示_什么是python倍数生成器
  7. 华硕主板无盘启动bios设置_【华硕主板bios启动顺序】华硕主板bois启动项_华硕主板bios硬盘启动...
  8. 计算机休眠查询,可不可以查询电脑待机记录
  9. matlab空间重采样,matlab重采样函数
  10. 18.链表插入——尾插法