Qt通过Doc模式读取XML并设计一个增删改查方便的操作类

简介: Qt通过Doc模式读取XML并设计一个增删改查方便的操作类

DOC 文档对象模型

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。它是一种与平台和语言无关的应用程序接口(API),它可以动态地访问程序和脚本,更新其内容、结构和www文档的风格(HTML和XML文档是通过说明部分定义的)。文档可以进一步被处理,处理的结果可以加入到当前的页面。


DOM是一种基于树的API文档,它要求在处理过程中整个文档都表示在存储器中。另外一种简单的API是基于事件的SAX,它可以用于处理很大的XML文档,由于大,所以不适合全部放在存储器中处理。

先来了解一下xml基本要素:


QtXML基本结构

下面列出了Qt处理xml的一些类以及说明,加粗表示是常用。

1687272933129.png


操作XML

部署环境

通过Qt Create创建一个工程。然后在*.pro配置文件中添加xml model。

在添加Qt解析xml相关的头文件

#include <QtXml>
#include <QDomDocument>


添加信息头

  // xml文档
  QDomDocument* m_Doc = new QDomDocument(); 
    // 创建XML处理类,通常用于处理第一行描述信息
    QDomProcessingInstruction instruction;
    std::string note =  "version=1.0 encoding=utf-8";
    // 创建XML头部格式
    instruction = m_Doc->createProcessingInstruction(QString::fromStdString(type), QString::fromStdString(note));
    // 添加到XML文件中
    m_Doc->appendChild(instruction);

读取XML文件

    // 打开文件
    QFile m_File = new QFile("./1.xml");
    if (false == m_File->open(mode))
    {
        QMessageBox::information(NULL, u8"提示", u8"文件打开失败!");
        return;
    }
    // 将doc与file关联起来
    // 这个函数从IO设备dev中读取XML文档,如果内容被成功解析,则返回true;否则返回false。
    if (false == m_Doc->setContent(m_File))
    {
        QMessageBox::information(NULL, u8"提示", u8"操作的文件不是XML文件!");
        m_File->close();
        return ;
    }


添加根节点

通过createElement方法创建的第一个子节点就是根节点。然后通过appendChild方法将创建的节点添加到doc中。

    // 创建根节点
    QDomElement rootNode = m_Doc->createElement(rootName);
    // 添加到XML文件中
    m_Doc->appendChild(rootNode);

添加一个没有属性的节点

添加一个没有属性的节点的方法和添加根节点的方法一致。

    // 创建根节点
    QDomElement rootNode = m_Doc->createElement(rootName);
    // 添加到XML文件中
    m_Doc->appendChild(rootNode);

添加一个有属性的节点

    std::list<std::pair<std::string, std::string>> Attribute{{"ID","2"},{"name","张三"}};
    QDomElement node = m_Doc->createElement(nodeName);
    // 给节点创建属性
    for(const auto& elem : Attribute)
    {
        // 参数一是字符串,参数二可以是任何类型
        node.setAttribute(QString::fromStdString(elem.first), QString::fromStdString(elem.second));
    }
    rootNode.appendChild(node);


添加一个元素节点

    // 创建子元素
    QDomElement node = m_Doc->createElement(nodeName);
    // 设置尖括号中的值
    QDomText text = m_Doc->createTextNode(value);
    // 添加关系
    node.appendChild(text);
    root.appendChild(node);


给节点单独设置属性

setAttribute方法设置节点的属性名和属性值。

    QDomElement elemNode = m_Doc->createElement(QString::fromStdString(name));
    elemNode.setAttribute(QString::fromStdString(name), QString::fromStdString(value));
    node.appendChild(elemNode);


删除所有同名节点

removeChild方法使用需要注意

看下面官方文档说明

Removes oldChild from the list of children. oldChild must be a direct child of this node.
Returns a new reference to oldChild on success or a null node on failure.


类似于C++容器存在迭代器失效问题。所以我这里就从后向前删除了。

    // 获取节点名字为Book的所有节点
    QDomNodeList nodes = m_Doc->elementsByTagName(nodeName);
    for (int i = nodes.size() - 1; i >= 0; i--)
    {
        // 获取节点
        QDomElement element = nodes.at(i).toElement();
         //   所以 要么从后向前删除 要么就需要重新指定新节点的位置
        root.removeChild(element);
    }


删除所有同名带属性的节点

    QDomNodeList nodes = m_Doc->elementsByTagName(nodeName);
    for (int i = 0; i < nodes.count(); i++)
    {
        // 获取节点
        QDomElement element = nodes.at(i).toElement();
        bool isFind {true};
        for(const auto& elem : attribute)
        {
            // 进行判断(返回属性的值进行判断)
            if (element.attribute(QString::fromStdString(elem.first)) != QString::fromStdString(elem.second))
            {
                isFind = false;
            }
        }
        if(true == isFind)
        {
            root.removeChild(nodes.at(i));
        }
    }


递归删除所有同名的节点

    // 获取节点名字为Book的所有节点
    QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename));
    // QDomElement root = GetRootNode();
    for (int i = nodes.size() - 1; i >= 0; i--)
    {
        // 获取节点
        QDomElement element = nodes.at(i).toElement();
        // 获取父节点
        QDomNode fNode = element.parentNode();
        fNode.removeChild(element);
    }


保存XML

    QFile* m_File= new QFile(fileName);
    if (false == m_File->open(QFileDevice::WriteOnly | QFileDevice::Truncate))
    {
        QMessageBox::information(NULL, "提示", "文件打开失败!");
        return;
    }
    QTextStream stream(m_File);
    m_Doc->save(stream, retract);


获取节点元素

if (node.toElement().tagName() == name)
{
    return node;
}


获取节点元素的值

if (node.toElement().tagName() == name)
{
    return node.toElement().text().toStdString();
}


获取该节点的属性值

key为属性名

if (false == node.isNull() && true == node.toElement().hasAttribute(key))
{
value = node.toElement().attributeNode(key).value();
}


完整代码

注:必须支持C++17

OperateXML.h

#ifndef OPERATEXML_H
#define OPERATEXML_H
// 读取xml必须要包含的
#include <QtXml>
#include <QDomDocument>
#include <QIODevice>
#include <QMessageBox>
//#include <QtCore5Compat/QTextCodec>
#include <string>
#include <map>
class OperateXML
{
public:
    explicit OperateXML(/*std::string filename = std::string("")*/);
    ~OperateXML();
    // 创建一个xml文件
    bool CreateXmlFile(std::string filename = std::string(""), QIODevice::OpenMode mode = QFileDevice::WriteOnly | QFileDevice::Truncate);
    // 添加一个信息头
    void CreateXmlHeader(std::string type = std::string("xml"), std::string version = "1.0", std::string encoding = std::string("UTF-8"));
    // 加载已有的xml文件
    bool LoadXml(std::string filename, QIODevice::OpenMode mode = QFileDevice::ReadOnly);
    // 获取根节点 Stu
    QDomElement GetRootNode();
    // 创建一个根节点  Stu
    QDomElement CreateXmlRootNode(std::string rootName = std::string("root"));
    // 添加一个没有属性的节点  <StuInfo/>  Stu StuInfo
    QDomElement AddNoAttributesNode(QDomElement fNode, std::string nodeName);
    // 添加一个有属性的节点  <StuInfo ID = "1">  Stu StuInfo  ID 1  Name 张三
    QDomElement AddAttributesNode(QDomElement fNode, std::string nodeName,
                                  std::list<std::pair<std::string, std::string>> Attribute);
    // 添加元素节点
    QDomElement  AddElementNode(QDomElement fNode, std::string nodeName, std::string value);
    // 设置节点属性
    QDomElement SetNodeAttribute(QDomElement node, std::string name, std::string value);
    // 删除节点 删除当前节点下的所有名为nodename的节点
    void DeleteNodes(QDomElement fNode, std::string nodename);
    // 删除节点 删除当前节点下的所有名为nodename的节点 且属性值相同
    void DeleteNode(QDomElement fNode, std::string nodename,
                    std::list<std::pair<std::string, std::string>> attribute);
    // 删除节点 删除所有节点下名为nodename的节点
    void DeleteNodes(std::string nodename);
     // 删除节点 删除所有节点下名为nodename的节点 且属性值相同
    void DeleteNode(std::string nodename, std::list<std::pair<std::string, std::string>> attribute);
    // 保存Xml  进位空格数
    void SaveXml(int retract = 4);
    // 获取当前节点以及同级下的兄弟节点 且名为name的节点  如果为第一个孩子节点记得传入第三个参数为true
    QDomElement GetElem(QDomElement& node, std::string name, bool first = false);
    QDomElement GetElem(QDomElement& node, QString name, bool first = false);
    // 获取当前节点以及同级下的兄弟节点 且名为name的节点的值
    std::string GetElemValue(QDomElement& node, std::string name, bool first = false);
    QString GetElemValue(QDomElement& node, QString name, bool first = false);
    // 获取 该节点指定的 属性节点值
    std::string GetElemAttributeValue(QDomElement node, std::string key);
    QString GetElemAttributeValue(QDomElement node, QString key);
private:
    QDomDocument* m_Doc;
    QFile* m_File;
    std::string m_FileName;
};
#endif // OPERATEXML_H 


OperateXML.cpp

#include "OperateXML.h"
OperateXML::OperateXML(/*std::string filename*/)
    : m_Doc(new QDomDocument())
    , m_File{nullptr}
{
}
OperateXML::~OperateXML()
{
    if(nullptr != m_Doc)
    {
        delete m_Doc;
        m_Doc = nullptr;
    }
    if(nullptr != m_File)
    {
        m_File->close();
        delete m_File;
        m_File = nullptr;
    }
}
bool OperateXML::CreateXmlFile(std::string filename, QIODevice::OpenMode mode)
{
    if(true == filename.empty())
    {
        QMessageBox::information(NULL, u8"提示", u8"文件名为空!");
        return false;
    }
    if(nullptr != m_File)
    {
        delete m_File;
        m_File = nullptr;
    }
    m_File = new QFile(QString::fromStdString(filename));
    if(false == m_File->open(mode))
    {
        QMessageBox::information(NULL, u8"提示", u8"文件打开或创建失败!");
        return false;
    }
    if(false == m_File->isOpen())
    {
        QMessageBox::information(NULL, u8"提示", u8"文件打开或创建失败!");
        return false;
    }
    return true;
}
void OperateXML::CreateXmlHeader(std::string type, std::string version, std::string encoding)
{
    // 创建XML处理类,通常用于处理第一行描述信息
    QDomProcessingInstruction instruction;
    std::string note =  std::string("version=\"") + version + std::string("\"  ") +
            std::string("encoding=\"") + encoding + std::string("\"");
    // 创建XML头部格式
    instruction = m_Doc->createProcessingInstruction(QString::fromStdString(type), QString::fromStdString(note));
    // 添加到XML文件中
    m_Doc->appendChild(instruction);
}
bool OperateXML::LoadXml(std::string filename, QIODevice::OpenMode mode)
{
    // 打开文件
    m_File = new QFile(QString::fromStdString(filename));
    if (false == m_File->open(mode))
    {
        QMessageBox::information(NULL, u8"提示", u8"文件打开失败!");
        return false;
    }
    // 将doc与file关联起来
    // 这个函数从IO设备dev中读取XML文档,如果内容被成功解析,则返回true;否则返回false。
    if (false == m_Doc->setContent(m_File))
    {
        QMessageBox::information(NULL, u8"提示", u8"操作的文件不是XML文件!");
        m_File->close();
        return false;
    }
    return true;
}
QDomElement OperateXML::GetRootNode()
{
    QDomElement root = m_Doc->documentElement();
    return root;
}
QDomElement OperateXML::CreateXmlRootNode(std::string rootName)
{
    // 创建根节点
    QDomElement rootNode = m_Doc->createElement(QString::fromStdString(rootName));
    // 添加到XML文件中
    m_Doc->appendChild(rootNode);
    return rootNode;
}
QDomElement OperateXML::AddNoAttributesNode(QDomElement fNode, std::string nodeName)
{
    if(true == fNode.isNull())
    {
        return QDomElement();
    }
    QDomElement node = m_Doc->createElement(QString::fromStdString(nodeName));
    fNode.appendChild(node);
    return node;
}
QDomElement OperateXML::AddAttributesNode(QDomElement fNode, std::string nodeName,
                                          std::list<std::pair<std::string, std::string>> Attribute)
{
    QDomElement node = m_Doc->createElement(QString::fromStdString(nodeName));
    // 给节点创建属性
    for(const auto& elem : Attribute)
    {
        // 参数一是字符串,参数二可以是任何类型
        node.setAttribute(QString::fromStdString(elem.first), QString::fromStdString(elem.second));
    }
    fNode.appendChild(node);
    return node;
}
QDomElement OperateXML::AddElementNode(QDomElement fNode, std::string nodeName, std::string value)
{
    // 创建子元素
    QDomElement node = m_Doc->createElement(QString::fromStdString(nodeName));
    // 设置尖括号中的值
    QDomText text = m_Doc->createTextNode(QString::fromStdString(value));
    // 添加关系
    node.appendChild(text);
    fNode.appendChild(node);
    return node;
}
QDomElement OperateXML::SetNodeAttribute(QDomElement node, std::string name, std::string value)
{
    QDomElement elemNode = m_Doc->createElement(QString::fromStdString(name));
    elemNode.setAttribute(QString::fromStdString(name), QString::fromStdString(value));
    node.appendChild(elemNode);
    return node;
}
void OperateXML::DeleteNodes(QDomElement fNode, std::string nodename)
{
    // 获取节点名字为Book的所有节点
    QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename));
    for (int i = nodes.size() - 1; i >= 0; i--)
    {
        // 获取节点
        QDomElement element = nodes.at(i).toElement();
        /*
         * Removes oldChild from the list of children. oldChild must be a direct child of this node.
            Returns a new reference to oldChild on success or a null node on failure.
            所以 要么从后向前删除 要么就需要重新指定新节点的位置
         */
        fNode.removeChild(element);
    }
}
void OperateXML::DeleteNode(QDomElement fNode, std::string nodename, std::list<std::pair<std::string, std::string>> attribute)
{
    QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename));
    for (int i = 0; i < nodes.count(); i++)
    {
        // 获取节点
        QDomElement element = nodes.at(i).toElement();
        bool isFind {true};
        for(const auto& elem : attribute)
        {
            // 进行判断(返回属性的值进行判断)
            if (element.attribute(QString::fromStdString(elem.first)) != QString::fromStdString(elem.second))
            {
                isFind = false;
            }
        }
        if(true == isFind)
        {
            fNode.removeChild(nodes.at(i));
        }
    }
}
void OperateXML::DeleteNodes(std::string nodename)
{
    // 获取节点名字为Book的所有节点
    QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename));
    // QDomElement root = GetRootNode();
    for (int i = nodes.size() - 1; i >= 0; i--)
    {
        // 获取节点
        QDomElement element = nodes.at(i).toElement();
        /*
         * Removes oldChild from the list of children. oldChild must be a direct child of this node.
            Returns a new reference to oldChild on success or a null node on failure.
            所以 要么从后向前删除 要么就需要重新指定新节点的位置
         */
        QDomNode fNode = element.parentNode();
        fNode.removeChild(element);
    }
}
void OperateXML::DeleteNode(std::string nodename, std::list<std::pair<std::string, std::string> > attribute)
{
    QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename));
    for (int i = 0; i < nodes.count(); i++)
    {
        // 获取节点
        QDomElement element = nodes.at(i).toElement();
        bool isFind {true};
        for(const auto& elem : attribute)
        {
            // 进行判断(返回属性的值进行判断)
            if (element.attribute(QString::fromStdString(elem.first)) != QString::fromStdString(elem.second))
            {
                isFind = false;
            }
        }
        if(true == isFind)
        {
            QDomNode fNode = element.parentNode();
            fNode.removeChild(nodes.at(i));
        }
    }
}
void OperateXML::SaveXml(int retract)
{
    m_File->close();
    if (false == m_File->open(QFileDevice::WriteOnly | QFileDevice::Truncate))
    {
        QMessageBox::information(NULL, "提示", "文件打开失败!");
        return;
    }
    QTextStream stream(m_File);
    m_Doc->save(stream, retract);
}
QDomElement OperateXML::GetElem(QDomElement& node, std::string name, bool first)
{
    if(true == first)
    {
        node = node.firstChild().toElement();
    }
    while(false == node.isNull())
    {
        if (node.toElement().tagName() == QString::fromStdString(name))
        {
            return node;
        }
        node = node.nextSibling().toElement();
    }
    return QDomElement();
}
QDomElement OperateXML::GetElem(QDomElement &node, QString name, bool first)
{
    if(true == first)
    {
        node = node.firstChild().toElement();
    }
    while(false == node.isNull())
    {
        // qDebug() << node.toElement().tagName();
        if (node.toElement().tagName() == name)
        {
            return node;
        }
        node = node.nextSibling().toElement();
    }
    return QDomElement();
}
std::string OperateXML::GetElemValue(QDomElement& node, std::string name, bool first)
{
    if(true == first)
    {
        node = node.firstChild().toElement();
    }
    while(false == node.isNull())
    {
        // qDebug() << node.toElement().tagName();
        if (node.toElement().tagName() == QString::fromStdString(name))
        {
            return node.toElement().text().toStdString();
        }
        node = node.nextSibling().toElement();
    }
    return std::string();
}
QString OperateXML::GetElemValue(QDomElement &node, QString name, bool first)
{
    if(true == first)
    {
        node = node.firstChild().toElement();
    }
    while(false == node.isNull())
    {
        // qDebug() << node.toElement().tagName();
        if (node.toElement().tagName() == name)
        {
            return node.toElement().text();
        }
        node = node.nextSibling().toElement();
    }
    return QString();
}
std::string OperateXML::GetElemAttributeValue(QDomElement node, std::string key)
{
    std::string value;
    if (false == node.isNull() && true == node.toElement().hasAttribute(QString::fromStdString(key)))
    {
        value = node.toElement().attributeNode(QString::fromStdString(key)).value().toStdString();
    }
    return value;
}
QString OperateXML::GetElemAttributeValue(QDomElement node, QString key)
{
    QString value;
    if (false == node.isNull() && true == node.toElement().hasAttribute(key))
    {
        value = node.toElement().attributeNode(key).value();
    }
    return value;
}
目录
相关文章
|
5天前
Qt类结构分析
Qt类结构分析
25 3
|
3月前
|
XML Java 数据格式
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
35 1
|
23天前
|
编解码 开发框架
【Qt 学习笔记】Qt窗口 | Qt窗口介绍 | QMainwindow类及各组件介绍
【Qt 学习笔记】Qt窗口 | Qt窗口介绍 | QMainwindow类及各组件介绍
64 3
|
23天前
|
容器
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Group Box的使用及说明
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Group Box的使用及说明
51 3
|
23天前
|
容器
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Tab Widget的使用及说明
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Tab Widget的使用及说明
17 2
|
23天前
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Slider的使用及说明
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Slider的使用及说明
68 2
|
23天前
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Dial的使用及说明
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Dial的使用及说明
25 2
|
23天前
|
数据可视化
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Date/Time Edit的使用及说明
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Date/Time Edit的使用及说明
85 2
|
23天前
【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Push Button的使用及说明
【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Push Button的使用及说明
59 0
【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Push Button的使用及说明
|
23天前
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Spin Box的使用及说明
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Spin Box的使用及说明
63 0