• [转]VC++中操作XML(MFC、SDK)


    XML在Win32程序方面应该没有在Web方面应用得多,很多Win32程序也只是用XML来存存配置信息而已,而且没有足够的好处的话还不如用ini。VC++里操作XML有两个库可以用:MSXML和XmlLite。MSXML又细分了两种接口:DOM和SAX2。XP没自带有XmlLite,只自带有2.x、3.x版的MSXML,不支持SAX2(需要MSXML 4.0以上),所以优先使用DOM。
    DOM是以COM形式提供的,VC++里调用DOM可以分3种方法:
    1、MFC里用CComPtr调用
    2、SDK里直接调用DOM接口
    3、SDK里用智能指针调用
    这3种方法本质上是一样的,区别只不过在于需要编码的多少而已,用CComPtr可以极大的简化代码,下面是几个例子。
    例子stocks.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <root>
    <node1>text1</node1>
    <node2>
    <childnode1 attrib1="value1" attrib2="value2"/>
    <childnode2 attrib1="value1" attrib2="value2">childtext1</childnode2>
    </node2>
    </root>


    这个例子应该包含了XML最常见的特征了吧?

    MFC
    MFC里可以直接使用DOM,不需要手动添加额外的头文件,只需要在CWinApp::InitInstance()里调用CoInitialize(NULL)初始化COM,在CWinApp::ExitInstance里调用CoUninitialize()释放COM就行了。

    //读取XML
    CComPtr<IXMLDOMDocument> spDoc; //DOM
    spDoc.CoCreateInstance(CLSID_DOMDocument);
    VARIANT_BOOL vb;
    spDoc->load(CComVariant(OLESTR("stocks.xml")), &vb); //加载XML文件
    CComPtr<IXMLDOMElement> spRootEle;
    spDoc->get_documentElement(&spRootEle); //根节点
    CComPtr<IXMLDOMNodeList> spNodeList;
    spRootEle->get_childNodes(&spNodeList); //子节点列表
    long nLen;
    spNodeList->get_length(&nLen); //子节点数
    for (long i = 0; i != nLen; ++i) //遍历子节点
    {
    CComPtr<IXMLDOMNode> spNode;
    spNodeList->get_item(i, &spNode);
    ProcessNode(spNode); //节点处理函数
    }

    //写入XML
    CComPtr<IXMLDOMNode> spNode;
    spRootEle->selectSingleNode(OLESTR("/root/node1"), &spNode);
    spNode->put_text(OLESTR("newText")); //写入text
    spRootEle->selectSingleNode(OLESTR("/root/node2/childnode1/@attrib1"), &spNode);
    spNode->put_nodeValue(CComVariant(OLESTR("newValue"))); //写入value
    CComPtr<IXMLDOMNode> spNewNode;
    spDoc->createNode(CComVariant(NODE_ELEMENT), OLESTR("childnode3"), OLESTR(""), &spNewNode); //创建新节点
    spRootEle->selectSingleNode(OLESTR("/root/node2"), &spNode);
    spNode->appendChild(spNewNode, &spNewNode); //将新节点加为node2的子节点
    spNewNode->put_text(OLESTR("childtext2")); //写入新节点text
    CComQIPtr<IXMLDOMElement> spEle = spNewNode; //注意这里使用CComQIPtr
    spEle->setAttribute(OLESTR("attrib1"), CComVariant(OLESTR("value1")));//给新节点添加属性
    spDoc->save(CComVariant(OLESTR("stocks.xml")));

    //节点处理函数
    void ProcessNode(CComPtr<IXMLDOMNode>& spNode)
    {
    CComBSTR bsNodeName;
    spNode->get_nodeName(&bsNodeName); //节点名
    AfxMessageBox(COLE2CT(bsNodeName));
    CComVariant varVal;
    spNode->get_nodeValue(&varVal); //节点值
    AfxMessageBox(COLE2CT(varVal.bstrVal));

    DOMNodeType eNodeType;
    spNode->get_nodeType(&eNodeType);
    if (eNodeType == NODE_ELEMENT) //只有NODE_ELEMENT类型才能包含有属性和子节点
    {
    //递归遍历节点属性
    CComPtr<IXMLDOMNamedNodeMap> spNameNodeMap;
    spNode->get_attributes(&spNameNodeMap);
    long nLength;
    spNameNodeMap->get_length(&nLength);
    for (long i = 0; i != nLength; ++i)
    {
    CComPtr<IXMLDOMNode> spNodeAttrib; //注意属性也是一个IXMLDOMNode
    spNameNodeMap->get_item(i, &spNodeAttrib);
    ProcessNode(spNodeAttrib);
    }

    //递归遍历子节点
    CComPtr<IXMLDOMNodeList> spNodeList;
    spNode->get_childNodes(&spNodeList);
    spNodeList->get_length(&nLength);
    for (long i = 0; i != nLength; ++i)
    {
    CComPtr<IXMLDOMNode> spChildNode;
    spNodeList->get_item(i, &spChildNode);
    ProcessNode(spChildNode);
    }
    }
    }


    对于<tag>text</tag>这样的节点,get_nodeValue会得到空,要得到"text"的话可以遍历子节点(只有一个子节点,它的nodeName为"#text",nodeType为NODE_TEXT,nodeValue就是"text");也可以用get_text直接得到"text",但是对于这样的节点<tag>text<childtag>childtext</childtag></tag>,get_text会同时得到"text"和"childtext",不过这样的节点应该是不允许的。
    DOM里使用的字符串(BSTR)都是OLESTR类型,默认情况下OLESTR是Unicode字符,MFC里可以用COLE2CT把LPCOLESTR转换为LPCTSTR。
    对于自己定义的XML,大多数时候不需要遍历,可以通过调用selectNodes、selectSingleNode指定XPath直接读取某个节点或属性:

    CComPtr<IXMLDOMDocument> spDoc; //DOM
    spDoc.CoCreateInstance(CLSID_DOMDocument);
    VARIANT_BOOL vb;
    spDoc->load(CComVariant(OLESTR("stocks.xml")), &vb); //加载XML文件
    CComPtr<IXMLDOMElement> spRootEle;
    spDoc->get_documentElement(&spRootEle); //根节点

    CComPtr<IXMLDOMNodeList> spNodeList;
    CComPtr<IXMLDOMNode> spNode;
    spRootEle->selectNodes(OLESTR("/root/node2/*"), &spNodeList); //得到node2下的所有子节点
    spRootEle->selectSingleNode(OLESTR("/root/node2/childnode1/@attrib1"), &spNode); //得到childnode1的attrib1属性


    XPath的语法可以参考XML文档或MSDN。

    SDK
    SDK中也可以使用智能指针,和MFC没太大区别,同样很方便,直接给代码:


    #include <iostream>
    #include <tchar.h>

    #import <msxml3.dll>

    //节点处理函数
    void ProcessNode(MSXML2::IXMLDOMNodePtr spNode)
    {
    std::cout << "nodeName: " << spNode->nodeName;
    if (spNode->nodeType == NODE_ATTRIBUTE || spNode->nodeType == NODE_TEXT)
    std::cout << " nodeValue: " << _bstr_t(spNode->nodeValue);
    std::cout << std::endl;

    if (spNode->nodeType == NODE_ELEMENT)
    {
    MSXML2::IXMLDOMNamedNodeMapPtr spNameNodeMap = spNode->attributes;
    for (long i = 0; i != spNameNodeMap->length; ++i) //遍历节点属性
    ProcessNode(spNameNodeMap->item[i]);

    MSXML2::IXMLDOMNodeListPtr spNodeList = spNode->childNodes;
    for (long i = 0; i != spNodeList->length; ++i) //遍历子节点
    ProcessNode(spNodeList->item[i]);
    }
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
    CoInitialize(NULL);
    //读取XML
    MSXML2::IXMLDOMDocumentPtr spXMLDoc;
    spXMLDoc.CreateInstance(__uuidof(MSXML2::DOMDocument30));
    spXMLDoc->load(L"stocks.xml");
    MSXML2::IXMLDOMElementPtr spRoot = spXMLDoc->documentElement; //根节点
    MSXML2::IXMLDOMNodeListPtr spNodeList = spRoot->childNodes;
    for (long i = 0; i != spNodeList->length; ++i) //遍历子节点
    ProcessNode(spNodeList->item[i]);

    //写入XML
    spRoot->selectSingleNode(L"/root/node1")->text = L"newText";
    spRoot->selectSingleNode(L"/root/node2/childnode1/@attrib1")->nodeValue = L"newValue";
    MSXML2::IXMLDOMNodePtr spNewNode = spRoot->selectSingleNode(L"/root/node2")->appendChild(
    spXMLDoc->createNode(_variant_t(NODE_ELEMENT), L"childnode3", L"")
    ); //给node2创建新子节点childnode3
    spNewNode->text = L"childtext2";
    MSXML2::IXMLDOMElementPtr spEle = spNewNode;
    spEle->setAttribute(L"attrib1", _variant_t(L"value1")); //添加新属性
    spXMLDoc->save(_variant_t(L"stocks.xml"));

    spNewNode.Release();
    spEle.Release();
    spNodeList.Release();
    spRoot.Release();
    spXMLDoc.Release();
    CoUninitialize();

    system("pause");
    return 0;
    }
  • 相关阅读:
    LR 场景设置
    win7 快捷键
    P1903 [国家集训队]数颜色 / 维护队列(莫队区间询问+单点修改)
    A
    P1494 [国家集训队]小Z的袜子(莫队)
    P2709 小B的询问(莫队入门)
    G
    #6285. 数列分块入门 9(区间的最小众数 离散化+数列分块)
    #6284. 数列分块入门 8(区间询问等于一个数 cc 的元素,并将这个区间的所有元素改为 c)
    #6283. 数列分块入门 7(区间乘法,区间加法,单点询问)
  • 原文地址:https://www.cnblogs.com/xumaojun/p/8528628.html
Copyright © 2020-2023  润新知