Qt学习(十九)—— XML

676 阅读7分钟

XML是用来存储数据的,另外,XML标签很灵活,可以由我们自己来定义,比如,在XML中存储一些学生数据:

<?xml version="1.0" encoding="UTF-8"?>
<info>
	<stu id="1">
		<name>joey</name>
		<sex>male</sex>
		<score>10</score>
	</stu>
	<stu id="2">
		<name>chandler</name>
		<sex>male</sex>
		<score>20</score>
	</stu>
</info>

P.S:这里学生用stu表示只是举例,用sutdent或者其它也可以。

正因为XML非常灵活,所以很多程序的配置文件都喜欢用XML。而且,XML可以支持很多种语言,比如C、C++、Java等等。

P.S:Qt中的.ui文件打开也是一个XML文件,所以在设计器中对窗口的组件进行移动或者其它操作,就可能会重写这个XML文件。

创建一个空XML文件

在Qt中要创建一个XML文件,需要先在.pro文件中添加:QT += xml,或者直接将 xml 添加在现有的QT += .. 语句后面,比如QT += core gui xml,然后构建一下我们的项目。

接着,在项目中新建一个C++类,但是不继承任何类。
在这里插入图片描述
在这里插入图片描述
然后,在该类的头文件中定义一个用来创建XML文件的函数,假设叫createXML(),函数参数是文件路径filePath,为了更加方便地调用这个函数,而不是每次通过对象调用,可以用static关键字修饰它,把它写成类的静态函数。

//DomXML.h
#ifndef DOMXML_H
#define DOMXML_H
#include <QString>

class DomXML
{
public:
    DomXML();
    static void createXML(QString filePath);    //创建XML空文件
    
};

#endif // DOMXML_H

说明:

  • 操作文件肯定需要用到QFile,所以要先引入<QFile>头文件。
  • 创建一个XML文档需要引入<QDomDocument>头文件。
  • XML文件的第一行<?xml version="1.0" encoding="UTF-8"?>是一段XML文件声明,生成文件声明需要先引入<QDomProcessingInstruction>头文件。
  • 在XML文件中,我们能够直接操作的对象叫作元素,要操作XML文件中的元素,还需要引入<QDomElement>头文件。

createXML(filePath)函数的意义是在本地路径为filePath的地方创建一个XML文件。

梳理一下思路:

1、在指定的路径下创建一个文件

  1. 创建一个文件对象,并为文件对象关联路径,也就是filePath。
  2. 调用open()打开文件对象,设置读写方式。

2、创建一个XML文档对象

  1. 创建一个XML文档对象
  2. XML文档对象调用createProcessingInstruction(),生成文档声明。但是这一步只能获取一个文档声明标签,此时它还没有被添加到DOM树中。
  3. XML文档对象调用appendChild(),把文档声明标签添加到DOM树中。
  4. XML文档对象调用createElement(),生成其它DOM元素。
  5. 根据实际需要,调用appendChild(),为元素建立父子关系,从而将生成的DOM元素添加到DOM树中。

3、将XML文档对象中的内容写入文件

  1. 创建一个QTextStream文本流对象,并与第一步的文件对象关联。
  2. XML文档对象调用sava(),将内容保存到文本流对象中,同时还可以设置缩进字符。
  3. 关闭文件。

DomXML.h代码同上,没有进行修改。

//DomXML.cpp
#include "DomXML.h"
#include <QFile>
#include <QDomDocument> //文件
#include <QDomProcessingInstruction> //文件头部
#include <QDomElement>  //文件元素
#include <QTextStream>  //文档流
DomXML::DomXML()
{

}
void DomXML::createXML(QString filePath){
    //一、创建一个文件
    QFile file(filePath);   //关联文件路径
    file.open(QIODevice::WriteOnly);    //打开文件并设置读写方式,这里是只写

    //二、创建XML文档对象
    QDomDocument doc;   //创建XML文档对象
    QDomProcessingInstruction ins=doc.createProcessingInstruction("xml","version=\'1.0\' encoding=\'UTF-8\'");  //生成文档声明
    doc.appendChild(ins);   //将文档声明添加到DOM树
    QDomElement root=doc.createElement("stu");  //生成根元素
    doc.appendChild(root);  //将根元素添加到DOM树
    QDomElement name=doc.createElement("name");
    root.appendChild(name); //元素name作为元素root的子元素被添加到DOM树中

    //三、保存
    QTextStream stream(&file);   //关联文件与文档流
    doc.save(stream,4); //将DOM文档保存到文档流中,并设置缩进字符为4
    file.close(); //关闭文件
}

实现效果:在这里插入图片描述
在这里插入图片描述
说明:

createProcessingInstruction()函数用来生成一个文档声明,它有两个参数,第一个参数是处理指令的目标target,第二个参数是处理指令的内容文本data。在上面的代码中,第一个参数我们传入的是"xml",第二个参数传入的是"version=“1.0” encoding=“UTF-8"”(字符串中的双引号还要用\进行转义),此函数就会自动生成<?xml version="1.0" encoding="UTF-8"?>这样一个节点。

实现appendXML(XML文件的读写)

首先要明确,要对XML文档对象中的元素进行操作,得先读取文件,在程序中获得XML文档对象,进行一番操作之后,再将更新后的XML文档对象写入文件。

再来梳理一下思路:
1、打开一个文件

  1. 创建QFile文件对象,并关联路径
  2. 调用open(),以只读的方式打开文件

2、获取文件内容

  1. 创建QDomDocument对象
  2. 调用setContent(),将文件与XML文档对象关联

3、创建节点元素

  1. 要创建一个节点元素,首先要调用documentElement(),获取XML文档对象的根节点元素
  2. 调用hasChildNodes(),判断根节点是否有子节点,如果没有,进行3 ~ 4步,如果有,调用lastChildElement()获取最后一个子节点。再进行5 ~ 10步。
  3. 取出根(父)节点下,目标节点元素(待添加的节点元素)所属的子节点。
  4. 判断子节点是否还有子节点,如果是,进行第 3 步,否则,进行第 5 步。
  5. 调用createElement(),创建一个节点元素。
  6. 调用createAttribute(),创建一个元素属性,并用一个QDomAttr属性对象来接收。
  7. 调用setNodeValue(),为属性对象设置属性值。
  8. 调用setAttributeNode(),将属性添加到节点元素中。
  9. 调用appendChild(),与它的上级节点元素建立父子关系,将其挂到DOM树上。【不要忘记!】
  10. 调用close(),关闭文件【不要忘记!!】

4、写入文件并保存

  1. 对XML文档对象的节点元素进行一番操作之后,最终还要把它写回文件去,调用open()打开之前的文件,设置读写方式为只写。
  2. 创建一个TextStream文本流对象,并与文件进行关联。
  3. 调用save(),将XML文档对象的内容保存到文本流对象中,并设置缩进字符。
  4. 调用close(),关闭文件。

注意:每次要换一种读写方式打开文件之前,都要先关闭文件!!!!否则就会有如下提示:

在这里插入图片描述
代码:

//DomXML.h
#ifndef DOMXML_H
#define DOMXML_H
#include <QString>
#include <QStringList>
class DomXML
{
public:
    DomXML();
    static void createXML(QString filePath);    //创建XML空文件
    static void appendXML(QString filePath);   //添加新节点

};

#endif // DOMXML_H
//DomXML.cpp
#include "DomXML.h"
#include <QFile>
#include <QDomDocument> //文件
#include <QDomProcessingInstruction> //文件头部
#include <QDomElement>  //文件元素
#include <QTextStream>  //文档流
DomXML::DomXML()
{

}
void DomXML::createXML(QString filePath){
    //一、创建一个文件
    QFile file(filePath);   //关联文件路径
    file.open(QIODevice::WriteOnly);    //打开文件并设置读写方式

    //二、创建XML文档对象
    QDomDocument doc;   //创建XML文档对象
    QDomProcessingInstruction ins=doc.createProcessingInstruction("xml","version=\'1.0\' encoding=\'UTF-8\'");  //生成文档声明
    doc.appendChild(ins);   //将文档声明添加到DOM树
    QDomElement root=doc.createElement("stu");  //生成根元素
    doc.appendChild(root);  //将根元素添加到DOM树

    //三、保存
    QTextStream stream(&file);   //关联文件与文档流
    doc.save(stream,4); //将DOM文档保存到文档流中,并设置缩进字符为4
    file.close();   //关闭文件
}
void DomXML::appendXML(QString filePath){
    //一、打开文件
    QFile file(filePath);   //关联文件路径
    file.open(QIODevice::ReadOnly); //打开文件并设置读写方式

    //二、获取文件内容
    QDomDocument doc;   //创建XML文档对象
    doc.setContent(&file);   //关联文件和XML文档对象
    file.close();   //关闭文件

    //三、创建节点元素
    file.open(QIODevice::WriteOnly);
    QDomElement root=doc.documentElement();
    if(root.hasChildNodes()){
        QDomElement elem=root.firstChildElement();  //假设要创建的节点在根节点的第一个子节点下
        //遍历到最深层
        while(elem.hasChildNodes()){
            elem=elem.firstChildElement();
        }
        //此时elem没有子节点了,便生成一个子节点挂在它下面
        QDomElement newElem=doc.createElement("newElement");
        QDomAttr attr=doc.createAttribute("attributeKey");
        attr.setNodeValue("attributeValue");
        newElem.setAttributeNode(attr);
        elem.appendChild(newElem);
        file.close();
    }else{
        //没有子节点元素,那就在这里创建子节点元素
        QDomElement elem=doc.createElement("name"); //创建节点元素
        QDomAttr attr=doc.createAttribute("type");  //创建属性元素
        attr.setNodeValue("english");   //设置属性值
        elem.setAttributeNode(attr);    //将属性添加到节点元素中
        root.appendChild(elem); //把当前节点挂到DOM树中
        file.close();   //关闭文件
    }

    //四、保存文件
    file.open(QIODevice::WriteOnly);    //以只写方式打开文件
    QTextStream stream(&file);  //关联文件与文本流
    doc.save(stream,4); //将XML文档对象保存到文本流中
    file.close();   //关闭文件
}
//Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H
//Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
#include "DomXML.h"
#pragma execution_character_set("utf-8")
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    DomXML::createXML("C:/Users/MSI-NB/Desktop/test.xml");	//创建XML文件
    DomXML::appendXML("C:/Users/MSI-NB/Desktop/test.xml");	//添加子节点
    DomXML::appendXML("C:/Users/MSI-NB/Desktop/test.xml");	//添加子节点
}

Widget::~Widget()
{
    delete ui;
}

实现效果:
在这里插入图片描述
说明:由于在代码中,默认要创建的目标节点在根节点的第一个子节点下,于是两次调用appenXML()方法得到的是这样的结果。在实际应用中,一般会通过判断标签名、属性名或属性值来获取我们需要的节点元素。

P.S:如有错误,欢迎指正~