1. 引言
MSXML(Microsoft XML Core Services)是微软提供的一套用于处理XML的COM组件库,广泛应用于Windows平台的XML解析、验证、转换等操作。本文将详细介绍如何从零开始,在C++中使用MSXML解析和操作XML文件,包含完整的代码示例及逐行注释,帮助初学者快速上手。
2. 准备工作
2.1 环境要求
- Windows 操作系统(MSXML 是 Windows 特有组件)
- Visual Studio(推荐 2015 或更高版本)
- 基础C++知识(COM组件基本概念)
2.2 引入MSXML库
在代码中引入MSXML头文件,并链接对应的库:
cpp
#include <msxml6.h> // MSXML 6.0(最新稳定版)
#include <comdef.h> // 用于COM智能指针(_bstr_t, _variant_t)
#pragma comment(lib, "msxml6.lib") // 链接MSXML库
3. 初始化COM环境
MSXML基于COM(Component Object Model),使用前必须初始化COM库:
cpp
#include <objbase.h> // CoInitializeEx 所需头文件
int main() {
// 初始化COM库(单线程模式)
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) {
printf("Failed to initialize COM. Error: %08X\n", hr);
return 1;
}
// 后续代码...
// 释放COM库
CoUninitialize();
return 0;
}
逐行解析:
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)
:初始化COM,指定单线程单元(STA)模式。FAILED(hr)
:检查COM初始化是否成功。CoUninitialize()
:程序结束时释放COM资源。
4. 加载并解析XML文件
4.1 创建MSXML DOM对象
cpp
IXMLDOMDocument2* pXMLDoc = NULL;
hr = CoCreateInstance(
__uuidof(DOMDocument60), // 使用MSXML 6.0
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IXMLDOMDocument2),
(void**)&pXMLDoc
);
if (FAILED(hr) || pXMLDoc == NULL) {
printf("Failed to create XML DOM instance. Error: %08X\n", hr);
CoUninitialize();
return 1;
}
逐行解析:
CoCreateInstance
:创建MSXML DOM对象。__uuidof(DOMDocument60)
:指定使用MSXML 6.0。CLSCTX_INPROC_SERVER
:组件运行在进程内。
4.2 加载XML文件
cpp
VARIANT_BOOL bSuccess = VARIANT_FALSE;
hr = pXMLDoc->load(_variant_t(L"example.xml"), &bSuccess);
if (FAILED(hr) || bSuccess != VARIANT_TRUE) {
printf("Failed to load XML file. Error: %08X\n", hr);
pXMLDoc->Release();
CoUninitialize();
return 1;
}
逐行解析:
pXMLDoc->load
:加载XML文件(example.xml
)。_variant_t(L"example.xml")
:将文件名转换为COM兼容的VARIANT
类型。bSuccess
:返回加载是否成功。
5. 读取XML数据
5.1 获取XML根节点
cpp
IXMLDOMElement* pRoot = NULL;
hr = pXMLDoc->get_documentElement(&pRoot);
if (FAILED(hr) {
printf("Failed to get root element. Error: %08X\n", hr);
pXMLDoc->Release();
CoUninitialize();
return 1;
}
逐行解析:
get_documentElement
:获取XML的根节点。
5.2 遍历子节点
cpp
IXMLDOMNodeList* pNodes = NULL;
hr = pRoot->get_childNodes(&pNodes);
if (FAILED(hr)) {
printf("Failed to get child nodes. Error: %08X\n", hr);
pRoot->Release();
pXMLDoc->Release();
CoUninitialize();
return 1;
}
long length = 0;
pNodes->get_length(&length); // 获取子节点数量
for (long i = 0; i < length; i++) {
IXMLDOMNode* pNode = NULL;
pNodes->get_item(i, &pNode); // 获取第i个节点
BSTR nodeName;
pNode->get_nodeName(&nodeName); // 获取节点名称
printf("Node %d: %S\n", i, nodeName);
SysFreeString(nodeName); // 释放BSTR内存
pNode->Release(); // 释放节点
}
pNodes->Release();
逐行解析:
get_childNodes
:获取所有子节点。get_length
:获取子节点数量。get_item(i, &pNode)
:获取第i
个节点。get_nodeName
:获取节点名称(返回BSTR
,需手动释放)。
6. 修改XML数据
6.1 修改节点属性
cpp
IXMLDOMElement* pFirstChild = NULL;
pNodes->get_item(0, (IXMLDOMNode**)&pFirstChild); // 获取第一个子节点
// 设置属性
hr = pFirstChild->setAttribute(
_bstr_t(L"newAttr"), // 属性名
_variant_t(L"newValue") // 属性值
);
if (FAILED(hr)) {
printf("Failed to set attribute. Error: %08X\n", hr);
}
pFirstChild->Release();
逐行解析:
setAttribute
:设置节点属性,使用_bstr_t
和_variant_t
封装字符串。
6.2 保存修改后的XML
cpp
hr = pXMLDoc->save(_variant_t(L"modified.xml"));
if (FAILED(hr)) {
printf("Failed to save XML. Error: %08X\n", hr);
}
逐行解析:
save
:保存XML到新文件。
7. 完整代码示例
演示1
cpp
#include <msxml6.h>
#include <comdef.h>
#include <objbase.h>
#include <stdio.h>
int main() {
// 初始化COM
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) {
printf("COM init failed. Error: %08X\n", hr);
return 1;
}
// 创建DOM对象
IXMLDOMDocument2* pXMLDoc = NULL;
hr = CoCreateInstance(
__uuidof(DOMDocument60),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IXMLDOMDocument2),
(void**)&pXMLDoc
);
if (FAILED(hr) || !pXMLDoc) {
printf("Failed to create DOM. Error: %08X\n", hr);
CoUninitialize();
return 1;
}
// 加载XML
VARIANT_BOOL bSuccess = VARIANT_FALSE;
hr = pXMLDoc->load(_variant_t(L"example.xml"), &bSuccess);
if (FAILED(hr) || bSuccess != VARIANT_TRUE) {
printf("Failed to load XML. Error: %08X\n", hr);
pXMLDoc->Release();
CoUninitialize();
return 1;
}
// 获取根节点
IXMLDOMElement* pRoot = NULL;
hr = pXMLDoc->get_documentElement(&pRoot);
if (FAILED(hr)) {
printf("Failed to get root. Error: %08X\n", hr);
pXMLDoc->Release();
CoUninitialize();
return 1;
}
// 遍历子节点
IXMLDOMNodeList* pNodes = NULL;
hr = pRoot->get_childNodes(&pNodes);
if (FAILED(hr)) {
printf("Failed to get child nodes. Error: %08X\n", hr);
pRoot->Release();
pXMLDoc->Release();
CoUninitialize();
return 1;
}
long length = 0;
pNodes->get_length(&length);
for (long i = 0; i < length; i++) {
IXMLDOMNode* pNode = NULL;
pNodes->get_item(i, &pNode);
BSTR nodeName;
pNode->get_nodeName(&nodeName);
printf("Node %d: %S\n", i, nodeName);
SysFreeString(nodeName);
pNode->Release();
}
// 修改节点属性
IXMLDOMElement* pFirstChild = NULL;
pNodes->get_item(0, (IXMLDOMNode**)&pFirstChild);
hr = pFirstChild->setAttribute(_bstr_t(L"newAttr"), _variant_t(L"newValue"));
if (FAILED(hr)) {
printf("Failed to set attribute. Error: %08X\n", hr);
}
// 保存XML
hr = pXMLDoc->save(_variant_t(L"modified.xml"));
if (FAILED(hr)) {
printf("Failed to save XML. Error: %08X\n", hr);
}
// 释放资源
pFirstChild->Release();
pNodes->Release();
pRoot->Release();
pXMLDoc->Release();
CoUninitialize();
return 0;
}
演示2
cpp
// Windows 头文件,提供基础API支持
#include <windows.h>
// MSXML 6.0 接口定义头文件
#include <msxml6.h>
// COM 基础功能头文件,包含CoInitialize等函数
#include <objbase.h>
// 标准输入输出头文件
#include <stdio.h>
// 添加以下头文件
#include <comutil.h>
// 添加以下链接库
#pragma comment(lib, "comsuppw.lib")
// 链接器指令:自动链接必要的库文件
#pragma comment(lib, "msxml6.lib") // MSXML 6.0 库
#pragma comment(lib, "ole32.lib") // COM 基础库
int main() {
/*****************************************************************
* 第一部分:COM库初始化和XML文档对象创建
*****************************************************************/
// 初始化COM库,所有使用COM技术的程序都必须先调用此函数
// 参数NULL表示使用默认的线程模型(单线程公寓STA)
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
printf("错误:COM库初始化失败。错误代码 = 0x%08x\n", hr);
return -1;
}
// 创建XML文档对象指针,初始化为NULL
IXMLDOMDocument2* pXMLDoc = NULL;
// 使用CoCreateInstance创建MSXML DOM文档对象
// 参数说明:
// CLSID_DOMDocument60 - MSXML 6.0 DOM文档的类ID
// NULL - 不进行聚合
// CLSCTX_INPROC_SERVER - 创建进程内服务器
// IID_IXMLDOMDocument2 - 请求的接口ID
// (void**)&pXMLDoc - 接收接口指针的地址
hr = CoCreateInstance(
CLSID_DOMDocument60,
NULL,
CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument2,
(void**)&pXMLDoc);
// 检查创建是否成功
if (FAILED(hr) || pXMLDoc == NULL) {
printf("错误:创建DOM文档对象失败。错误代码 = 0x%08x\n", hr);
CoUninitialize(); // 释放COM库
return -1;
}
/*****************************************************************
* 第二部分:设置文档属性
*****************************************************************/
// 设置异步加载为FALSE(同步加载)
// VARIANT_FALSE表示禁用异步加载
hr = pXMLDoc->put_async(VARIANT_FALSE);
// 设置验证文档结构(如果文档有DTD或Schema)
// VARIANT_TRUE表示开启验证
hr = pXMLDoc->put_validateOnParse(VARIANT_TRUE);
// 设置不解析外部资源(如外部DTD)
// VARIANT_FALSE表示不解析外部资源
hr = pXMLDoc->put_resolveExternals(VARIANT_FALSE);
// 设置保留空白字符
// VARIANT_TRUE表示保留空白字符(如缩进、换行等)
hr = pXMLDoc->put_preserveWhiteSpace(VARIANT_TRUE);
/*****************************************************************
* 第三部分:创建和加载XML文档
*****************************************************************/
// 创建一个包含XML内容的BSTR字符串
// BSTR是COM中使用的字符串类型,以长度前缀和null结尾
BSTR bstrXML = SysAllocString(
L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
L"<bookstore>\n"
L" <book category=\"cooking\">\n"
L" <title lang=\"en\">Everyday Italian</title>\n"
L" <author>Giada De Laurentiis</author>\n"
L" <year>2005</year>\n"
L" <price>30.00</price>\n"
L" </book>\n"
L" <book category=\"children\">\n"
L" <title lang=\"en\">Harry Potter</title>\n"
L" <author>J.K. Rowling</author>\n"
L" <year>2005</year>\n"
L" <price>29.99</price>\n"
L" </book>\n"
L" <book category=\"web\">\n"
L" <title lang=\"en\">Learning XML</title>\n"
L" <author>Erik T. Ray</author>\n"
L" <year>2003</year>\n"
L" <price>39.95</price>\n"
L" </book>\n"
L"</bookstore>"
);
// 用于接收加载是否成功的变量
VARIANT_BOOL vbIsSuccessful;
// 将XML字符串加载到文档对象中
hr = pXMLDoc->loadXML(bstrXML, &vbIsSuccessful);
// 释放BSTR内存(COM编程中必须手动释放分配的内存)
SysFreeString(bstrXML);
// 检查XML加载是否成功
if (FAILED(hr) || vbIsSuccessful != VARIANT_TRUE) {
printf("错误:加载XML字符串失败。\n");
// 获取解析错误对象
IXMLDOMParseError* pXMLError = NULL;
BSTR bstrError = NULL;
// 获取解析错误信息
hr = pXMLDoc->get_parseError(&pXMLError);
if (SUCCEEDED(hr)) {
// 获取错误原因描述
pXMLError->get_reason(&bstrError);
printf("错误原因: %S\n", bstrError);
// 释放错误描述字符串
SysFreeString(bstrError);
// 释放错误对象
pXMLError->Release();
}
// 清理资源
pXMLDoc->Release();
CoUninitialize();
return -1;
}
/*****************************************************************
* 第四部分:演示从文件加载XML(补充功能)
*****************************************************************/
// 创建另一个XML文档对象用于演示文件加载
IXMLDOMDocument2* pXMLDocFromFile = NULL;
hr = CoCreateInstance(CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument2, (void**)&pXMLDocFromFile);
if (SUCCEEDED(hr) && pXMLDocFromFile != NULL) {
// 设置文档属性(同上)
pXMLDocFromFile->put_async(VARIANT_FALSE);
pXMLDocFromFile->put_validateOnParse(VARIANT_TRUE);
// 创建VARIANT变量来指定文件名
VARIANT varFileName;
VariantInit(&varFileName); // 初始化VARIANT
varFileName.vt = VT_BSTR; // 设置类型为BSTR
// 分配文件名(假设当前目录下有books.xml文件)
varFileName.bstrVal = SysAllocString(L"books.xml");
// 从文件加载XML
hr = pXMLDocFromFile->load(varFileName, &vbIsSuccessful);
// 清理VARIANT
VariantClear(&varFileName);
if (SUCCEEDED(hr) && vbIsSuccessful == VARIANT_TRUE) {
printf("成功从文件加载XML文档。\n");
// 这里可以添加对加载文档的操作...
} else {
printf("警告:从文件加载XML失败,可能文件不存在。\n");
}
// 释放文档对象
pXMLDocFromFile->Release();
}
/*****************************************************************
* 第五部分:遍历XML文档
*****************************************************************/
// 获取文档根元素
IXMLDOMElement* pRootElem = NULL;
hr = pXMLDoc->get_documentElement(&pRootElem);
if (SUCCEEDED(hr) && pRootElem != NULL) {
// 获取根元素名称
BSTR bstrRootName;
hr = pRootElem->get_nodeName(&bstrRootName);
if (SUCCEEDED(hr)) {
printf("根元素名称: %S\n", bstrRootName);
SysFreeString(bstrRootName);
}
/*****************************************************************
* 演示创建处理指令(补充功能)
*****************************************************************/
// 创建处理指令节点
IXMLDOMProcessingInstruction* pPI = NULL;
BSTR bstrTarget = SysAllocString(L"xml-stylesheet");
BSTR bstrData = SysAllocString(L"type=\"text/xsl\" href=\"style.xsl\"");
hr = pXMLDoc->createProcessingInstruction(bstrTarget, bstrData, &pPI);
if (SUCCEEDED(hr) && pPI != NULL) {
// 将处理指令插入到文档开头
IXMLDOMNode* pInsertedNode = NULL;
hr = pXMLDoc->insertBefore(pPI, _variant_t(pRootElem), &pInsertedNode);
if (SUCCEEDED(hr)) {
printf("成功添加处理指令。\n");
pInsertedNode->Release();
}
pPI->Release();
}
SysFreeString(bstrTarget);
SysFreeString(bstrData);
/*****************************************************************
* 演示创建注释(补充功能)
*****************************************************************/
// 创建注释节点
IXMLDOMComment* pComment = NULL;
BSTR bstrComment = SysAllocString(L"这是一个示例XML文档");
hr = pXMLDoc->createComment(bstrComment, &pComment);
if (SUCCEEDED(hr) && pComment != NULL) {
// 将注释插入到根元素之前
IXMLDOMNode* pInsertedComment = NULL;
hr = pXMLDoc->insertBefore(pComment, _variant_t(pRootElem), &pInsertedComment);
if (SUCCEEDED(hr)) {
printf("成功添加注释节点。\n");
pInsertedComment->Release();
}
pComment->Release();
}
SysFreeString(bstrComment);
/*****************************************************************
* 遍历book节点
*****************************************************************/
// 获取所有book元素
IXMLDOMNodeList* pBooks = NULL;
BSTR bstrTagName = SysAllocString(L"book");
hr = pRootElem->getElementsByTagName(bstrTagName, &pBooks);
SysFreeString(bstrTagName);
if (SUCCEEDED(hr) && pBooks != NULL) {
// 获取book元素数量
long bookCount;
pBooks->get_length(&bookCount);
printf("找到 %d 本书\n", bookCount);
// 遍历每本书
for (long i = 0; i < bookCount; i++) {
IXMLDOMNode* pBook = NULL;
hr = pBooks->get_item(i, &pBook);
if (SUCCEEDED(hr) && pBook != NULL) {
// 获取整本书的XML内容
BSTR bstrBookXML;
pBook->get_xml(&bstrBookXML);
printf("\n第 %d 本书:\n%S\n", i + 1, bstrBookXML);
SysFreeString(bstrBookXML);
// 获取book元素的属性
IXMLDOMNamedNodeMap* pAttrs = NULL;
pBook->get_attributes(&pAttrs);
if (pAttrs != NULL) {
// 获取category属性
IXMLDOMNode* pCategoryAttr = NULL;
BSTR bstrAttrName = SysAllocString(L"category");
hr = pAttrs->getNamedItem(bstrAttrName, &pCategoryAttr);
SysFreeString(bstrAttrName);
if (SUCCEEDED(hr) && pCategoryAttr != NULL) {
VARIANT varValue;
VariantInit(&varValue);
// 获取属性值
pCategoryAttr->get_nodeValue(&varValue);
printf(" 类别: %S\n", varValue.bstrVal);
// 清理VARIANT
VariantClear(&varValue);
pCategoryAttr->Release();
}
pAttrs->Release();
}
/*****************************************************************
* 演示克隆节点(补充功能)
*****************************************************************/
// 克隆当前book节点(深拷贝,包括所有子节点)
IXMLDOMNode* pClonedBook = NULL;
hr = pBook->cloneNode(VARIANT_TRUE, &pClonedBook);
if (SUCCEEDED(hr) && pClonedBook != NULL) {
// 可以在此处操作克隆的节点...
printf(" 成功克隆本书节点。\n");
pClonedBook->Release();
}
/*****************************************************************
* 遍历子节点
*****************************************************************/
IXMLDOMNodeList* pChildNodes = NULL;
pBook->get_childNodes(&pChildNodes);
if (pChildNodes != NULL) {
long childCount;
pChildNodes->get_length(&childCount);
for (long j = 0; j < childCount; j++) {
IXMLDOMNode* pChild = NULL;
hr = pChildNodes->get_item(j, &pChild);
if (SUCCEEDED(hr) && pChild != NULL) {
// 获取子节点名称
BSTR bstrChildName;
pChild->get_nodeName(&bstrChildName);
// 特别处理price节点
if (wcscmp(bstrChildName, L"price") == 0) {
BSTR bstrPrice;
pChild->get_text(&bstrPrice);
printf(" 价格: %S\n", bstrPrice);
SysFreeString(bstrPrice);
}
SysFreeString(bstrChildName);
pChild->Release();
}
}
pChildNodes->Release();
}
pBook->Release();
}
}
pBooks->Release();
}
/*****************************************************************
* 演示删除节点(补充功能)
*****************************************************************/
// 查找要删除的节点(例如第一本书)
IXMLDOMNode* pBookToRemove = NULL;
BSTR bstrXPathRemove = SysAllocString(L"//book[1]");
hr = pXMLDoc->selectSingleNode(bstrXPathRemove, &pBookToRemove);
SysFreeString(bstrXPathRemove);
if (SUCCEEDED(hr) && pBookToRemove != NULL) {
// 从父节点中删除子节点
IXMLDOMNode* pRemovedNode = NULL;
hr = pRootElem->removeChild(pBookToRemove, &pRemovedNode);
if (SUCCEEDED(hr)) {
printf("\n成功删除第一本书。\n");
pRemovedNode->Release();
}
pBookToRemove->Release();
}
pRootElem->Release();
}
/*****************************************************************
* 第六部分:执行XPath查询
*****************************************************************/
// 查找价格大于35的书
BSTR bstrXPath = SysAllocString(L"//book[price>35.00]");
IXMLDOMNodeList* pExpensiveBooks = NULL;
hr = pXMLDoc->selectNodes(bstrXPath, &pExpensiveBooks);
SysFreeString(bstrXPath);
if (SUCCEEDED(hr) && pExpensiveBooks != NULL) {
long expensiveCount;
pExpensiveBooks->get_length(&expensiveCount);
printf("\n找到 %d 本价格高于35.00的书\n", expensiveCount);
// 遍历查询结果
for (long i = 0; i < expensiveCount; i++) {
IXMLDOMNode* pBook = NULL;
hr = pExpensiveBooks->get_item(i, &pBook);
if (SUCCEEDED(hr) && pBook != NULL) {
// 在每本书中查找title节点
BSTR bstrTitleXPath = SysAllocString(L"title");
IXMLDOMNode* pTitle = NULL;
// 注意:这里需要将pBook转换为IXMLDOMElement接口
hr = ((IXMLDOMElement*)pBook)->selectSingleNode(bstrTitleXPath, &pTitle);
SysFreeString(bstrTitleXPath);
if (SUCCEEDED(hr) && pTitle != NULL) {
BSTR bstrTitle;
pTitle->get_text(&bstrTitle);
printf(" 高价书标题: %S\n", bstrTitle);
SysFreeString(bstrTitle);
pTitle->Release();
}
pBook->Release();
}
}
pExpensiveBooks->Release();
}
/*****************************************************************
* 第七部分:修改XML内容
*****************************************************************/
// 查找所有book节点
BSTR bstrAllBooksXPath = SysAllocString(L"//book");
IXMLDOMNodeList* pAllBooks = NULL;
hr = pXMLDoc->selectNodes(bstrAllBooksXPath, &pAllBooks);
SysFreeString(bstrAllBooksXPath);
if (SUCCEEDED(hr) && pAllBooks != NULL) {
long bookCount;
pAllBooks->get_length(&bookCount);
for (long i = 0; i < bookCount; i++) {
IXMLDOMNode* pBook = NULL;
hr = pAllBooks->get_item(i, &pBook);
if (SUCCEEDED(hr) && pBook != NULL) {
// 创建discount元素
IXMLDOMElement* pDiscountElem = NULL;
BSTR bstrDiscountTag = SysAllocString(L"discount");
hr = pXMLDoc->createElement(bstrDiscountTag, &pDiscountElem);
SysFreeString(bstrDiscountTag);
if (SUCCEEDED(hr) && pDiscountElem != NULL) {
// 设置discount元素的文本内容
BSTR bstrDiscountValue = SysAllocString(L"10%");
hr = pDiscountElem->put_text(bstrDiscountValue);
SysFreeString(bstrDiscountValue);
// 将新元素添加到book节点
IXMLDOMNode* pInsertedNode = NULL;
hr = pBook->appendChild(pDiscountElem, &pInsertedNode);
if (SUCCEEDED(hr) && pInsertedNode != NULL) {
pInsertedNode->Release();
}
pDiscountElem->Release();
}
/*****************************************************************
* 演示替换节点(补充功能)
*****************************************************************/
// 查找year节点
BSTR bstrYearPath = SysAllocString(L"year");
IXMLDOMNode* pYearNode = NULL;
hr = ((IXMLDOMElement*)pBook)->selectSingleNode(bstrYearPath, &pYearNode);
SysFreeString(bstrYearPath);
if (SUCCEEDED(hr) && pYearNode != NULL) {
// 创建新的year节点
IXMLDOMElement* pNewYearElem = NULL;
BSTR bstrYearTag = SysAllocString(L"year");
hr = pXMLDoc->createElement(bstrYearTag, &pNewYearElem);
SysFreeString(bstrYearTag);
if (SUCCEEDED(hr) && pNewYearElem != NULL) {
// 设置新的年份值(原年份+1)
BSTR bstrOldYear;
pYearNode->get_text(&bstrOldYear);
int year = _wtoi(bstrOldYear) + 1;
wchar_t newYear[10];
swprintf(newYear, 10, L"%d", year);
BSTR bstrNewYear = SysAllocString(newYear);
pNewYearElem->put_text(bstrNewYear);
SysFreeString(bstrNewYear);
SysFreeString(bstrOldYear);
// 替换节点
IXMLDOMNode* pReplacedNode = NULL;
hr = pBook->replaceChild(pNewYearElem, pYearNode, &pReplacedNode);
if (SUCCEEDED(hr)) {
printf(" 成功更新第%d本书的出版年份。\n", i+1);
pReplacedNode->Release();
}
pNewYearElem->Release();
}
pYearNode->Release();
}
pBook->Release();
}
}
pAllBooks->Release();
}
/*****************************************************************
* 第八部分:保存修改后的XML
*****************************************************************/
// 创建VARIANT变量来指定输出文件名
VARIANT varOutputFile;
VariantInit(&varOutputFile); // 初始化VARIANT
varOutputFile.vt = VT_BSTR; // 设置类型为BSTR
varOutputFile.bstrVal = SysAllocString(L"modified_books.xml");
// 保存XML文档到文件
hr = pXMLDoc->save(varOutputFile);
if (FAILED(hr)) {
printf("错误:保存修改后的XML文件失败。\n");
} else {
printf("\n修改后的XML已保存到 modified_books.xml\n");
}
// 清理VARIANT变量
VariantClear(&varOutputFile);
/*****************************************************************
* 第九部分:清理资源
*****************************************************************/
// 释放XML文档对象
pXMLDoc->Release();
// 反初始化COM库
CoUninitialize();
// 暂停控制台窗口(方便查看输出)
system("pause");
return 0;
}
8. 总结
本文详细介绍了如何在C++中使用MSXML进行XML解析、遍历、修改和保存,并提供了完整的代码示例和逐行注释。关键点包括:
- 初始化COM环境 (
CoInitializeEx
)。 - 创建MSXML DOM对象 (
CoCreateInstance
)。 - 加载XML文件 (
load
)。 - 遍历和修改节点 (
get_childNodes
,setAttribute
)。 - 保存XML (
save
)。
通过本教程,可以快速掌握MSXML的基本用法,并应用于实际项目中。