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、在指定的路径下创建一个文件
- 创建一个文件对象,并为文件对象关联路径,也就是filePath。
- 调用open()打开文件对象,设置读写方式。
2、创建一个XML文档对象
- 创建一个XML文档对象
- XML文档对象调用createProcessingInstruction(),生成文档声明。但是这一步只能获取一个文档声明标签,此时它还没有被添加到DOM树中。
- XML文档对象调用appendChild(),把文档声明标签添加到DOM树中。
- XML文档对象调用createElement(),生成其它DOM元素。
- 根据实际需要,调用appendChild(),为元素建立父子关系,从而将生成的DOM元素添加到DOM树中。
3、将XML文档对象中的内容写入文件
- 创建一个QTextStream文本流对象,并与第一步的文件对象关联。
- XML文档对象调用sava(),将内容保存到文本流对象中,同时还可以设置缩进字符。
- 关闭文件。
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、打开一个文件
- 创建QFile文件对象,并关联路径
- 调用open(),以只读的方式打开文件
2、获取文件内容
- 创建QDomDocument对象
- 调用setContent(),将文件与XML文档对象关联
3、创建节点元素
- 要创建一个节点元素,首先要调用documentElement(),获取XML文档对象的根节点元素
- 调用hasChildNodes(),判断根节点是否有子节点,如果没有,进行3 ~ 4步,如果有,调用lastChildElement()获取最后一个子节点。再进行5 ~ 10步。
- 取出根(父)节点下,目标节点元素(待添加的节点元素)所属的子节点。
- 判断子节点是否还有子节点,如果是,进行第 3 步,否则,进行第 5 步。
- 调用createElement(),创建一个节点元素。
- 调用createAttribute(),创建一个元素属性,并用一个QDomAttr属性对象来接收。
- 调用setNodeValue(),为属性对象设置属性值。
- 调用setAttributeNode(),将属性添加到节点元素中。
- 调用appendChild(),与它的上级节点元素建立父子关系,将其挂到DOM树上。【不要忘记!】
- 调用close(),关闭文件【不要忘记!!】
4、写入文件并保存
- 对XML文档对象的节点元素进行一番操作之后,最终还要把它写回文件去,调用open()打开之前的文件,设置读写方式为只写。
- 创建一个TextStream文本流对象,并与文件进行关联。
- 调用save(),将XML文档对象的内容保存到文本流对象中,并设置缩进字符。
- 调用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:如有错误,欢迎指正~