[xml] MSXML6读/写/修改xml文件的简单实现

记录一下MSXML6是如何对xml文件进行读写和解析的。

参考文章

microsoft官方文档,里面有基础的代码示例。
拷贝示例代码到Virtual Studio 控制台工程,直接可以运行。
MSXML6 可以调用raw api也可以使用智能指针,我选择的智能指针。
Program with DOM in C-C++ Using Smart Pointer Class Wrappers
这篇文章有对官方示例代码和接口的解释,增删改查都有示例。
MSXM简单的使用
可以在vimsky搜索具体的代码实现示例
C++ IXMLDOMElementPtr::getAttribute方法代码示例

我的代码

因为官方的示例xml层级比较简单,我自己弄了个层级稍微深一点的xml,在测试用xml部分。
我使用的是Virtual Studio 2022。

↓头文件、导入MSXML库、以及全局变量等

#include <stdio.h>
#include <tchar.h>
#import <msxml6.dll>

enum LoadType
{
    eLoad_NoPrint = 0,  // load 时不进行打印
    eLoad_Print,        //load 时打印到控制台
};

class FileInfo
{
public:
    FileInfo() {}
    ~FileInfo() {}
public:
    MSXML2::IXMLDOMDocumentPtr m_pXMLDom;
    const char* m_pfilename = NULL;
};
void loadDOMsmart(FileInfo* fileinfo, enum LoadType lt);
void queryNodesSmart(FileInfo* fileinfo);
void PhaseSubNodes(MSXML2::IXMLDOMNodePtr& pNode, int level);
void ChangeXMLContent(FileInfo* fileinfo);
void SaveXMLContent(FileInfo* fileinfo);

↓主函数

int main(int argc, _TCHAR* argv[])
{    
    HRESULT hr = CoInitialize(NULL);
    if (SUCCEEDED(hr)) {
        FileInfo fileinfo;       
        //申请接口
        HRESULT hr = fileinfo.m_pXMLDom.CreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER); 
        if (FAILED(hr)) {
            printf("Failed to instantiate an XML DOM.\n");
            return -1;
        }
        fileinfo.m_pfilename = "Property.xml";       
        loadDOMsmart(&fileinfo, eLoad_NoPrint); //载入xml文件       
        queryNodesSmart(&fileinfo); //解析xml文件
        ChangeXMLContent(&fileinfo);//修改xml文件
        SaveXMLContent(&fileinfo); //保存xml文件

        //释放接口
        fileinfo.m_pXMLDom.Release();
        CoUninitialize();
    }
    return 0;
}

载入xml文件

//Load xml时,使用的是 MSXML2::IXMLDOMDocumentPtr
//IXMLDOMDocument: DOM树结构的根结点, IXMLDOMDocumentPtr是它的智能指针
void loadDOMsmart(FileInfo* fileinfo, enum LoadType lt)
{   
    try {
        //默认参数设置
        fileinfo->m_pXMLDom->async = VARIANT_FALSE;//是否异步执行load函数, 默认false        
        fileinfo->m_pXMLDom->validateOnParse = VARIANT_FALSE;//是否使用内部或外部 DTD 来验证文档。 默认值为 false。
        fileinfo->m_pXMLDom->resolveExternals = VARIANT_FALSE;//是否进行外部解析,默认为false
 #if 0 //使用LoadXML() 获取内存中的xml源
        //获取xml文件大小
        struct stat statbuff;
        //stat("D:\\SourceCode\\soap_test\\soap_test\\x64\\Debug\\Property.xml", &statbuff);
        stat(fileinfo->m_pfilename, &statbuff);
        int filesize = statbuff.st_size;
        if (filesize < 0) {
            printf("get %s size failed\n", fileinfo->m_pfilename);
            return;
        }
        fileinfo->m_ixmldatasize = filesize + 2;
        fileinfo->m_pxml_data = new char[fileinfo->m_ixmldatasize];
        memset((char*)fileinfo->m_pxml_data, 0x00 ,fileinfo->m_ixmldatasize);
      
        FILE* fp = NULL;
        errno_t err = fopen_s(&fp, fileinfo->m_pfilename, "rb"); //读取UTF16编码的文件,使用_wfopen_s更好
        if (err != 0) {
            printf("open %s failed\n", fileinfo->m_pfilename);
            return;
        }
        fseek(fp,2, SEEK_SET);//跳过bom:FFFE
        size_t fsize = fread_s(fileinfo->m_pxml_data, fileinfo->m_ixmldatasize, sizeof(char), filesize, fp);
        wchar_t* wptr = (wchar_t*)fileinfo->m_pxml_data;
        printf("read from %s, filesize %d, read size:%d\n", fileinfo->m_pfilename, filesize, (int)fsize);
        _bstr_t xml_data(fileinfo->m_pxml_data);
        VARIANT_BOOL ret = fileinfo->m_pXMLDom->loadXML(BSTR(wptr)); //xml 文件加载
#else //使用Load() 读取xml文件
        VARIANT_BOOL ret = fileinfo->m_pXMLDom->load(fileinfo->m_pfilename); //xml 文件加载
#endif
        if (ret == VARIANT_TRUE) {            
            if (lt == eLoad_Print)//打印xml数据流
                printf("XML DOM loaded from %s:\n%s\n", fileinfo->m_pfilename, (LPCSTR)fileinfo->m_pXMLDom->xml);
        }
        else {  // Failed to load xml           
            printf("Failed to load DOM from %s. %s\n", fileinfo->m_pfilename, (LPCSTR)fileinfo->m_pXMLDom->parseError->Getreason());
        }
    }
    catch (_com_error errorObject) {
        printf("Exception thrown, HRESULT: 0x%08x", errorObject.Error());
    }   
}

解析(遍历)

//解析一个xml文件,提取其中的元素
void queryNodesSmart(FileInfo* fileinfo)
{
    try {
        //遍历node
        BSTR bstr;
        long iNodesCount;
        MSXML2::IXMLDOMElementPtr pRootNode;
        MSXML2::IXMLDOMNodeListPtr pRootSubNodes;
        MSXML2::IXMLDOMNodePtr pNode;

        pRootNode = fileinfo->m_pXMLDom->GetdocumentElement();//获取根节点
        if (pRootNode == NULL) {
            printf("get root node failed\n");
            return;
        }
        bstr = pRootNode->GetnodeName();//输出根节点信息
        wprintf(L"Root:%s\n", (TCHAR*)bstr);
		
		//这里是通过GetchildNodes() 获取所有子节点,然后递归遍历
		//也可以使用GetfirstChild() + get_nextSibling() 来进行遍历。示例代码:
		//https://vimsky.com/examples/detail/cpp-ex-msxml2-IXMLDOMNodePtr-GetnextSibling-method.html
        pRootSubNodes = pRootNode->GetchildNodes(); //获取根节点的子节点列表        
        if (FAILED(pRootSubNodes->get_length(&iNodesCount))) { //获取根节点的子节点列表的长度
            printf("get nodes list length failed\n");
            return ;
        }
        
        for (int i = 0; i < iNodesCount; i++) { //遍历子节点列表
            pRootSubNodes->get_item(i, (MSXML2::IXMLDOMNode**)&pNode);       
            PhaseSubNodes(pNode, 1); //进入sub node 的递归
        }
    }
    catch (_com_error errorObject) {
        printf("Exception thrown, HRESULT: 0x%08x", errorObject.Error());
    }
    return;
}

递归函数:

void PhaseSubNodes(MSXML2::IXMLDOMNodePtr& pNode, int level)
{
    BSTR bstr;
    long icount;
    MSXML2::IXMLDOMNodePtr pSubNode;
    MSXML2::IXMLDOMNodePtr pNamedItem;
    MSXML2::IXMLDOMNodeListPtr pSubNodes;
    MSXML2::IXMLDOMNamedNodeMapPtr pAttributes;

    bstr = pNode->GetnodeName();
    TCHAR tabstring[64] = { 0x00 };
    for (int i = 0; i < level; i++) {
        wcscat_s(tabstring, 64, L"    "); //wcscat_s 第二个参数是TCHAR数量,不是字节数
    }

    //对不同节点名进行解析
    if (wcscmp((TCHAR*)bstr, L"PropertyMap") == 0) { //这个节点有属性,获取属性
        pNode->get_attributes((MSXML2::IXMLDOMNamedNodeMap**) & pAttributes);
        if (pAttributes != NULL) {
            pNamedItem = pAttributes->getNamedItem(BSTR(L"attrInfo"));
            wprintf(L"%sNode: % s attr:%s\n", tabstring, (TCHAR*)bstr, (TCHAR*)pNamedItem->text);
        }
        else
            wprintf(L"%sNode: % s\n", tabstring, (TCHAR*)bstr);
    }
    else if (wcscmp((TCHAR*)bstr, L"#text") == 0) { //这个节点名,表示已经最后一级,取出text,并且返回
        wprintf(L"%s#text:%s\n", tabstring, (TCHAR*)pNode->text);
        return;
    }
    else
        wprintf(L"%sNode: % s\n", tabstring, (TCHAR*)bstr);
    
    pNode->get_childNodes((MSXML2::IXMLDOMNodeList**)&pSubNodes);
    pSubNodes->get_length(&icount);
    for (int i = 0; i < icount; i++) { //遍历子节点列表
        pSubNodes->get_item(i, (MSXML2::IXMLDOMNode**)&pSubNode);
		PhaseSubNodes(pSubNode, level + 1); //调用递归函数
    }
}

遍历后的输出结果:
在这里插入图片描述

修改xml文件

//把第一个DeviceTypeID改成“unkonw”
void ChangeXMLContent(FileInfo* fileinfo)
{
    MSXML2::IXMLDOMNodePtr pSubNode;
    MSXML2::IXMLDOMNodeListPtr pSubNodes;
    MSXML2::IXMLDOMNamedNodeMapPtr pAttributes;
    //**修改指定节点的内容
     //从任意节点选择 Node名为:PropertyMap,获取第一个
    pSubNode = fileinfo->m_pXMLDom->selectSingleNode(L"//DeviceTypeID");
	if (pSubNode) {
        pSubNode->put_text(BSTR(L"unkonw"));
	}
	else {
	    printf("No node is fetched.\n");
	}

	//**给指定节点增加内容
    //从任意节点选择 Node名为:PropertyMap, 拥有attrInfo属性, 属性值为'Structure/PrivateBox/Origin/ReadOnly/的节点
    pSubNodes = fileinfo->m_pXMLDom->selectNodes(L"//PropertyMap[@attrInfo='Structure/PrivateBox/Origin/ReadOnly/']");
	if (pSubNodes) {
	    for (long i = 0; i < pSubNodes->length; i++) {
            pSubNode = pSubNodes->item[i];
            //从当前节点选择 AttributeValue 节点
            MSXML2::IXMLDOMNodePtr pNode_AttributeValue = pSubNode->selectSingleNode(L"AttributeValue");
            if (pNode_AttributeValue) {
                //从 AttributeValue 节点选择 Length 节点
                MSXML2::IXMLDOMNodePtr pNode_Length = pNode_AttributeValue->selectSingleNode(L"Length");
                if (pNode_Length) {
                    //Length 数值改为2
                    pNode_Length->put_text(BSTR(L"2"));
                }
                //从 AttributeValue 节点选择 AttributeType 节点
                MSXML2::IXMLDOMNodePtr pNode_AttributeType = pNode_AttributeValue->selectSingleNode(L"AttributeType");
                if (pNode_AttributeType) {
                    // 给AttributeType 节点增加内容
                    //创建一个新的Element
                    MSXML2::IXMLDOMElementPtr pNode_PropertyMap_new = fileinfo->m_pXMLDom->createElement(L"PropertyMap");
                    if (pNode_PropertyMap_new) {
                        //创建一个新的Attribute
                        MSXML2::IXMLDOMAttributePtr pAttribute =  fileinfo->m_pXMLDom->createAttribute(L"attrInfo");
                        if (pAttribute) {
                            //设置Attribute值
                            _variant_t value = L"Boolean/newadd/Origin/ReadOnly/";
                            pAttribute->put_value(value);  
                            //把Attribute添加到Element
                            pNode_PropertyMap_new->setAttributeNode(pAttribute);
                        }
                        //把新的Element添加到AttributeType节点
                        pNode_AttributeType->appendChild(pNode_PropertyMap_new);
                    }
                }               
            }            
	    }
	}
	else
	{
	    printf("No node set is fetched.\n");
	}
}

保存xml文件

void SaveXMLContent(FileInfo* fileinfo)
{
    try {
        if (FAILED(fileinfo->m_pXMLDom->save("Property_new.xml"))) {
            printf("Save fail: %s\n", (LPCSTR)fileinfo->m_pXMLDom->parseError->Getreason());
        }
        printf("Save succeed\n");
    }
    catch (_com_error e) {
        printf("Exception thrown, HRESULT: 0x%08x", e.Error());
    }
}

测试用xml

文件名:Property.xml

<?xml version="1.0" encoding="utf-8" ?>
<DeviceTypeDefaultList>
	<DeviceTypeDefault>
		<DeviceTypeIDList>
			<DeviceTypeID>1.3.6.1</DeviceTypeID>
			<DeviceTypeID>1.3.6.5</DeviceTypeID>
			<DeviceTypeID>1.3.6.9</DeviceTypeID>
		</DeviceTypeIDList>
		<PropertyMap attrInfo="Structure/Root/Origin/ReadOnly/">
			<AttributeValue>
				<Length>1</Length>
				<AttributeType>
					<PropertyMap attrInfo="List/Origin/ReadOnly/">
						<AttributeValue>
							<Length>0</Length>
							<AttributeType></AttributeType>
						</AttributeValue>
					</PropertyMap>
					<PropertyMap attrInfo="Structure/Origin/ReadOnly/">
						<AttributeValue>
							<Length>2</Length>
							<AttributeType>
								<PropertyMap attrInfo="Boolean/IsMD/Origin/ReadOnly/">
									<AttributeValue>false</AttributeValue>
								</PropertyMap>
								<PropertyMap attrInfo="Boolean/IsEOn/Origin/ReadOnly/">
									<AttributeValue>true</AttributeValue>
								</PropertyMap>
							</AttributeType>
						</AttributeValue>
					</PropertyMap>
				</AttributeType>
			</AttributeValue>
		</PropertyMap>
		<ListDefault>
			<PropertyMap attrInfo="Structure/PrivateBox/Origin/ReadOnly/">
				<AttributeValue>
					<Length>1</Length>
					<AttributeType>
						<PropertyMap attrInfo="Boolean/PwdExist/Origin/ReadOnly/">
							<AttributeValue>false</AttributeValue>
							<AttributeValueOld>false</AttributeValueOld>
						</PropertyMap>
					</AttributeType>
				</AttributeValue>
			</PropertyMap>
		</ListDefault>
	</DeviceTypeDefault>
</DeviceTypeDefaultList>

输入输出xml对比

的确修改了上面代码中修改的3个目标节点。
在这里插入图片描述

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: MFC(Microsoft Foundation Classes)是一个应用程序框架,可用于开发Windows操作系统下的C++应用程序。要实现通过MFC修改XML文件的数据,可以采用以下几个步骤: 1. 导入所需的MFC类库和头文件。 要使用MFC操作XML文件,需要在C++代码中包含afx.h头文件,并在项目设置中添加对MFC的依赖。 2. 载入XML文件。 使用MFC提供的CMarkup类或CXmlDocument类,先载入需要修改XML文件。通过调用Load或Open方法打开XML文件并将其加载到内存中。 3. 寻找需要修改的节点。 根据XML结构,使用MFC提供的节点遍历方法或XPath表达式,在XML文件中找到需要修改的特定节点。 4. 修改节点的属性或文本内容。 通过调用MFC提供的方法,可直接修改找到的节点的属性或文本内容。例如,使用SetAttrib或SetText方法设置节点的属性或文本。 5. 保存修改后的XML文件。 通过调用Save方法或SaveToFile方法,将修改后的XML文件保存到磁盘上。 下面是一个示例代码,以MFC和CMarkup类为例,演示修改XML文件中一个特定节点的属性: ```cpp #include "stdafx.h" #include "Markup.h" using namespace std; int main() { CMarkup xml; if (xml.Load("example.xml")) // 载入XML文件 { if (xml.FindElem("book")) // 找到book节点 { xml.SetAttrib("category", "new_category"); // 修改book节点的category属性值 xml.Save("modified_example.xml"); // 保存修改后的XML文件 } xml.Close(); } return 0; } ``` 以上是一个简单的示例,可根据实际需求和XML文件的复杂程度进行相应的修改和优化。通过MFC实现修改XML文件数据需要熟悉MFC框架和XML文件的结构,掌握相关的MFC类和方法,以便灵活操作XML文件中的数据。 ### 回答2: 在MFC中实现修改XML文件数据需要使用CMarkup类。CMarkup是MFC中的一个轻量级XML解析类,可以方便地取和修改XML文件。 首先,在程序中引入CMarkup类的头文件。 然后,使用CMarkup的Open方法打开XML文件,可以选择只模式。打开后,可以使用CMarkup的FindElem和FindChildElem方法定位到需要修改XML元素。 接下来,使用CMarkup的SetAttrib方法可以修改元素的属性值,使用SetData方法可以修改元素的文本值。 修改完成后,使用CMarkup的Save方法保存修改后的XML文件。 以下是一个简单的示例代码: ```cpp #include <afxmarkup.h> void ModifyXMLData() { CMarkup xml; if (xml.Load("data.xml")) { if (xml.FindElem("root")) { xml.IntoElem(); // 进入root元素 while (xml.FindElem("item")) { xml.SetAttrib("attribute", "new value"); xml.SetData("new data"); } } xml.Save("data.xml"); xml.Close(); } } ``` 这段代码假设XML文件名为data.xmlXML结构如下: ```xml <root> <item attribute="old value">old data</item> <item attribute="old value">old data</item> ... </root> ``` 代码中会将所有item元素的attribute属性修改为"new value",文本值修改为"new data",然后保存修改后的XML文件。 通过以上步骤,就可以在MFC中实现修改XML文件数据了。 ### 回答3: MFC(Microsoft Foundation Classes)是一种用于开发Windows应用程序的C++框架。要实现修改XML文件数据,可以按照以下步骤进行: 1. 使用MFC提供的CFile类打开XML文件。可以使用CFile的Open函数,指定文件路径和打开模式(例如模式)。 2. 创建一个XML文档对象。可以使用MSXML库提供的CComPtr类,通过调用其CreateInstance函数来创建一个用于解析和修改XML的DOM文档对象。 3. 使用DOM文档对象加载之前打开的XML文件。可以使用CComPtr类的Load函数,将XML文件加载到DOM文档对象中。 4. 使用DOM文档对象进行数据修改。可以使用CComPtr类提供的各种方法和属性,来获取和修改XML文件中的数据。例如,可以使用getElementsByTagName函数获取特定元素的集合,然后通过遍历集合来修改元素的属性或内容。 5. 将修改后的数据保存回XML文件。可以使用CComPtr类的Save函数,将DOM文档对象保存到同一路径的XML文件中。 6. 在完成修改后,关闭XML文件。可以使用CFile类的Close函数,关闭之前打开的XML文件。 7. 程序的其他部分需要根据具体需求进行设计,例如用户界面的布局和交互等。 总结起来,使用MFC实现修改XML文件数据的关键步骤包括:打开XML文件、创建DOM文档对象、加载XML文件修改数据、保存到XML文件以及关闭文件。使用CFile类和CComPtr类可以实现这些步骤,并结合MFC的其他功能来完成个性化的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值