资讯专栏INFORMATION COLUMN

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

Ashin / 1373人阅读

摘要:史上最最靠谱,又双叒叒简单的基于的解析指南最近做相关的项目,遇到同时使用和来解析文件中信息的问题,这类问题如果做开发也会经常用到。基于的技术,用于处理操作系统随附的。提供本机实现,同时支持和。包含基于事件的分析器。

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

最近做C++相关的项目,遇到同时使用COM和MSXML来解析XML文件中信息的问题,这类问题如果做MFC开发也会经常用到。
在网上搜了一整圈,确实很难找到可用的code,总算自己研究出高效而简单的方法,借此机会总结一下,并分享给大家。

附 VS Project镜像:
SimpleParser4MSXML-cpp: C++语言写的MSXML的简单使用示例, COM 和 MFC 开发中比较常用。
https://github.com/yanglr/Sim...
点击”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"
"
L"
"
L"  Hey
"
L"    
"
L"    
"
L"    
"
L"    
"
L"  
"
L"
";

然后从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
", ssName);   // 用%ls打印BSTR字符串内容
    SysFreeString(ssName);               // 用完字符串后必须释放      

    CComBSTR ssName;
    wprintf(L"Node name:%s
", ssName);   // 这里的L不能省略
    SysFreeString(ssName);

CComBSTR类字符串的内容复制到wstring中,然后使用wcout输出

   CComBSTR ssName;
   wstring bstrText(ssName);
   wcout << bstrText << 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;
主要代码
#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"
"
L"
"
L"  Hey
"
L"    
"
L"    
"
L"    
"
L"    
"
L"  
"
L"
";

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...


参考资料:

IXMLDOMElement接口

Using the MSXML Parser

MFC C++ XML Parse - Using MSXML

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

本文原载于本人csdn博客 →
https://blog.csdn.net/lzuacm/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110405.html

相关文章

  • 实用性前端知识 - 收藏集 - 掘金

    摘要:与面向对象编程六大方向助你突破前端生涯平台期前端掘金无论我们从事何种职业,在职业生涯的某个阶段,都或多或少会遇到所谓的平台期。目前为止,已经有个用户通过认证登观点年前端初学者的生存指南前端掘金逝者如斯夫,不舍昼夜。 你可能听说过函数式编程(Functional programming),甚至已经使用了一段时间。 但是,你能说清楚,它到底是什么吗? 网上搜索一下,你会轻松找到好多答案...

    Honwhy 评论0 收藏0
  • 双叒叕是一个动态简历

    摘要:标点处理根据传入的字符来判断下一个字符出现的延迟时间,即方法的第二个参数。年初的时候试着重写了这个项目,感觉已是没有什么难度了,不过也是面向过程,一顿操作罢了。 先看效果 请戳这里看预览这里是代码 见过了?别走,这是与众不同的地方 针对移动端优化了体验 支持动画跳过 支持多段动画 标点字符特殊处理,停留时间略长于字符时间 typescript 编写 对功能进行了封装处理,可以直接引入...

    firim 评论0 收藏0
  • 双叒叕是一个动态简历

    摘要:标点处理根据传入的字符来判断下一个字符出现的延迟时间,即方法的第二个参数。年初的时候试着重写了这个项目,感觉已是没有什么难度了,不过也是面向过程,一顿操作罢了。 先看效果 请戳这里看预览这里是代码 见过了?别走,这是与众不同的地方 针对移动端优化了体验 支持动画跳过 支持多段动画 标点字符特殊处理,停留时间略长于字符时间 typescript 编写 对功能进行了封装处理,可以直接引入...

    iKcamp 评论0 收藏0

发表评论

0条评论

Ashin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<