VC++ 2019 MFC TinyXML2使用教程/方法详解(转载)

发布时间 2023-11-21 17:26:29作者: ckrgd

转载地址:

VC++ 2019 MFC TinyXML2使用教程/方法详解_vc++2019_一笑的博客-CSDN博客

TinyXML2让VC++中操作XML,如鱼得水,就像一个小型的数据库,特别方便。

本篇主要介绍在VC++ 2019的MFC项目中,如何利用TinyXML2,创建、插入、查询、更新、删除节点或数据。

也顺便介绍下UNICODE转UTF8,因TinyXML2生成的XML文件是UTF-8编码,VS2019开发工具是UNICODE编码,所以,中文不转换会写入乱码。

一、创建MFC项目 MFCTinyXML2

  1. 应用程序类型选择“基于对话框”
  2. 主框架样式仅选择“粗框架”,其他样式无需选择
  3. 高级功能处全部全部不要选

二、下载源码并复制到MFC项目中

1、源码地址:https://github.com/leethomason/tinyxml2

 

2、将版本库中的tinyxml2.cpp和tinyxml2.h复制到项目中(源码中的文件很多,但,只需要这两个文件~,其他文件直接删掉就好了)

 


在Dialog界面增加几个按钮,最终项目目录结构如下

 


在主Dlg类中引用tinyxml2/tinyxml2.h

 


在项目中,选中tinyxml2.cpp文件,点击右键查看其属性,将“C/C++” - “预编译头” - 设置为“不使用预编译头”(如不设置,VS2019会“温馨”提示你没有使用pch.h这个预编译头文件)

 


OK,至此已可正常使用tinyxml2中的类和方法了。

三、创建XML,并保存到XML文件中

//使用以下内容建立XMLDocument
const char* xmlContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
tinyxml2::XMLDocument docXml;    
docXml.Parse(xmlContent);//添加root节点
tinyxml2::XMLElement* root = docXml.NewElement("root");
docXml.InsertEndChild(root);//添加message节点
tinyxml2::XMLElement* messageNode = docXml.NewElement("message");
messageNode->SetAttribute("username", "zhangsan");//属性
messageNode->SetText("hello");//内容
root->InsertEndChild(messageNode);//保存成XML文件
docXml.SaveFile("XMLFile.xml");

 

生成的XML文件,如下

<?xml version="1.0" encoding="utf-8"?>
<root><message username="zhangsan">hello</message>
</root>

 

四、读取XML

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");//读取上边生成的XML文件,如果还没有生成,会报错:load xml file failed.
if (xmlError == tinyxml2::XML_SUCCESS) {
    //获取root节点
    tinyxml2::XMLElement* root = docXml.RootElement();

    //获取message节点
    tinyxml2::XMLElement* messageNode = root->FirstChildElement("message");

    //获取message->username属性的值
    const char* username = messageNode->FindAttribute("username")->Value();

    //获取message的内容
    const char* content = messageNode->GetText();
    
    //弹窗展示读取到的信息
    CString str;
    str.Format(_T("<message username=\"%s\">%s</message>"), Char2Wchar(username), Char2Wchar(content));
    AfxMessageBox(str);
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

Char2Wchar转宽字节方法代码

wchar_t* Char2Wchar(const char* str, UINT CodePage) {
    //先获取转换成宽字符后的长度
    int nLen = MultiByteToWideChar(CodePage, MB_PRECOMPOSED, str, -1, NULL, 0);
    //声明一个宽字符类型变量,用于存放转换后的字符
    wchar_t* wstr = new wchar_t[nLen];
    //利用微软ANSI转宽字符的函数(name:ANSI字符,wname:宽字符)
    MultiByteToWideChar(CodePage, MB_PRECOMPOSED, str, -1, wstr, nLen);

    return wstr;
}

 

效果:XML文件不存在时,会提示

 


效果:读取成功后

 

五、新增节点

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
if (xmlError == tinyxml2::XML_SUCCESS) {
    //获取root节点
    tinyxml2::XMLElement* root = docXml.RootElement();

    //循环增加3次message节点
    const char* arrUsername[3] = {"lisi","wangwu","zhaoliu"};
    for (int i = 0; i < 3; i++)
    {
        //生成新的message节点(无论在哪个节点中New,都直接在doc.NewElement)
        tinyxml2::XMLElement* messageNode = docXml.NewElement("message");
        messageNode->SetAttribute("username", arrUsername[i]);//属性
        messageNode->SetText(i);//内容
        root->InsertEndChild(messageNode);
    }

    //不要忘记保存一下
    docXml.SaveFile("XMLFile.xml");
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

效果:增加了0/1/2三条message节点记录

 

六、查询

1、根据属性值(如:username = wangwu)查找
方法

/// <summary>
/// 根据属性/值查询节点
/// </summary>
/// <param name="root">xml root节点</param>
/// <param name="elementName">要查询的节点,一般是多行的</param>
/// <param name="attributeName">节点的属性名</param>
/// <param name="attributeValue">节点的属性值</param>
/// <returns></returns>
tinyxml2::XMLElement* queryXMLElementByAttribute(tinyxml2::XMLElement* root, const char* elementName, const char* attributeName, const char* attributeValue) {
    tinyxml2::XMLElement* node = root->FirstChildElement(elementName);
    while (node != NULL)
    {
        const char* value = node->Attribute(attributeName);
        //比较两个字符串char*是否相等(const char*,不能直接使用等号比较)
        if (strcmp(value, attributeValue) == 0)
            break;

        node = node->NextSiblingElement();
    }

    return node;
}

 

调用

const char* whereUsername = "wangwu";

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
if (xmlError == tinyxml2::XML_SUCCESS) {
    //获取root节点
    tinyxml2::XMLElement* root = docXml.RootElement();
    tinyxml2::XMLElement* messageNode = tinyxml2::XMLElement* messageNode = queryXMLElementByAttribute(root, "message", "username", "wangwu");

    //最后保留下的这个节点就是查询到的节点
    //弹窗展示读取到的信息
    CString str;
    str.Format(_T("<message username=\"%s\">%s</message>"), Char2Wchar(messageNode->Attribute("username")), Char2Wchar(messageNode->GetText()));
    AfxMessageBox(str);
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

效果:

 

2、根据值查找(同理)

七、更新

1、更新属性值

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
if (xmlError == tinyxml2::XML_SUCCESS) {
    //获取root节点
    tinyxml2::XMLElement* root = docXml.RootElement();

    //查询
    tinyxml2::XMLElement* messageNode = queryXMLElementByAttribute(root, "message", "username", "lisi");
    //更新
    messageNode->SetAttribute("username", "lisi1234");//改成lisi1234
    //不要忘记保存一下
    docXml.SaveFile("XMLFile.xml");
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

效果:

 

2、更新内容(同理)

八、删除

1、删除指定节点

tinyxml2::XMLDocument docXml;
    tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
    if (xmlError == tinyxml2::XML_SUCCESS) {
        //获取root节点
        tinyxml2::XMLElement* root = docXml.RootElement();

        //查询
        tinyxml2::XMLElement* messageNode = queryXMLElementByAttribute(root, "message", "username", "zhaoliu");
        //删除查询到的节点
        root->DeleteChild(messageNode);
        
        //不要忘记保存一下
        docXml.SaveFile("XMLFile.xml");
    }
    else {
        AfxMessageBox(_T("load xml file failed."));
    }

 

2、删除全部子节点
代码:

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
if (xmlError == tinyxml2::XML_SUCCESS) {
    //获取root节点
    tinyxml2::XMLElement* root = docXml.RootElement();
    //删除查询到的节点
    root->DeleteChildren();

    //不要忘记保存一下
    docXml.SaveFile("XMLFile.xml");
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

效果:

 

九、获取XML文件全部内容

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
if (xmlError == tinyxml2::XML_SUCCESS) {
    tinyxml2::XMLPrinter printer;
    docXml.Print(&printer);

    AfxMessageBox(Char2Wchar(printer.CStr()));
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

效果:

 

十、UTF-8编码的XML中,增加中文节点(中文乱码处理办法)

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
if (xmlError == tinyxml2::XML_SUCCESS) {
    //获取root节点
    tinyxml2::XMLElement* root = docXml.RootElement();

    //生成新的message节点(无论在哪个节点中New,都直接在doc.NewElement)
    tinyxml2::XMLElement* messageNode = docXml.NewElement("message");

    messageNode->SetAttribute("username", "王二麻子");//属性
    messageNode->SetText("中文内容测试");//内容

    root->InsertEndChild(messageNode);

    //不要忘记保存一下
    docXml.SaveFile("XMLFile.xml");
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

使用以上代码,不做任何处理,会出现如下情况(乱码)

 


之所以中文会乱码,分析原因为:VS是使用UNICODE编码(UTF16),直接用写入UTF8编码的XML文件中,当然会乱码,因此,需要做如下转换:

  1. ASCII(char*)转宽字节(wchar_t*/UNICODE/CP_ACP,因为微软有提供函数MultiByteToWideChar)
  2. UNICODE以UTF8(CP_UTF8)编码转ASCII

转换成UTF8编码的代码:

char* Ascii2Unicode2Utf8(const char* str) {
    //ASCII to Unicode(CP_ACP)
    int nLen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
    wchar_t* wstr = new wchar_t[nLen];
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstr, nLen);

    //Unicode to UTF-8
    int nLenUTF8 = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
    char* strUTF8 = new char[nLenUTF8];
    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, strUTF8, nLenUTF8, NULL, NULL);

    return strUTF8;
}

 

使用了转UTF8编码的方法后,生成XML不再乱码了

tinyxml2::XMLDocument docXml;
tinyxml2::XMLError xmlError = docXml.LoadFile("XMLFile.xml");
if (xmlError == tinyxml2::XML_SUCCESS) {
    //获取root节点
    tinyxml2::XMLElement* root = docXml.RootElement();

    //生成新的message节点(无论在哪个节点中New,都直接在doc.NewElement)
    tinyxml2::XMLElement* messageNode = docXml.NewElement("message");

    messageNode->SetAttribute("username", Ascii2Unicode2Utf8("王二麻子"));//属性
    messageNode->SetText(Ascii2Unicode2Utf8("中文内容测试"));//内容

    root->InsertEndChild(messageNode);

    //不要忘记保存一下
    docXml.SaveFile("XMLFile.xml");
}
else {
    AfxMessageBox(_T("load xml file failed."));
}

 

效果:中文字内容写入正常

 

十一、本篇文章项目源码

https://gitee.com/kefong/MFCTinyxml2.git