1. JSON概述
-
JSON(JavaScript Object Notation, JS 对象简谱)是一种轻量级的数据交换格式。
-
JSON 采用 key-value 的结构来组织和管理数据。
-
JSON 支持的数据类型: 数值型、字符串、布尔值、数组、对象等
- JSON 来源于 JavaScript
-
JSON应用领域: 不同系统间数据的传递、 应用程序的配置文件
a.json
补充说明:
-
以大括号包裹的数据叫做对象 {}
-
以方括号包裹的数据叫做数组 []
2. 读取 JSON
读取 json文档所需的类:
-
QJsonDocument:Json 文档类,能将 json 格式的字符串转为 JSON 对象
-
fromJson(jsonStr); 将json字符串转为 json 文档 (静态方法)
-
object(); 将json文档转为 json对象
-
-
QJsonObject: Json 对象类
-
keys: 获取 json对象中的所有key
-
value("key") : 根据 key 的名称获取对应的值
-
-
QJsonArray: Json 数组类
-
QJsonValue: Json 值
-
isString() \ isDouble() \ isObject() \ isArray()
-
toArray() : 将 key 对应的值转为数组
-
toObject() : 将 key 对应的值转为对象
-
示例1: 读取 userinfo.json 中基础类型数据
cpp
// 读取 json 文件
QFile file(":/files/userinfo.json");
if (!file.open(QIODevice::ReadOnly))
{
qDebug() << "json文件读取失败";
return;
}
QByteArray buf = file.readAll();
// 将 json字符串转为 json文档
QJsonDocument jsonDoc = QJsonDocument::fromJson(buf);
// 将json文档转为 json对象
QJsonObject rootObj = jsonDoc.object();
// 使用 value 方法,根据 key 值读取数据
qDebug() << rootObj.value("id") << rootObj.value("id").toInt();
qDebug() << rootObj.value("name") << rootObj.value("name").toString();
qDebug() << rootObj.value("isMarry") << rootObj.value("isMarry").toBool();
示例2: 读取 userinfo.json 中所有数据
cpp
QFile file(":/files/userinfo.json");
if (!file.open(QIODevice::ReadOnly))
{
qDebug() << "打开json文件失败";
return;
}
QString jsonStr = file.readAll();
// 将 json格式的字符串保存到 jsonDoc 中
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8());
QJsonObject root = jsonDoc.object();
// 获取根节点下的子节点
QStringList keys = root.keys();
// 遍历子节点
for (int i = 0; i < keys.size(); i++)
{
QJsonValue value = root.value(keys[i]);
if (value.isString())
{
qDebug() << "字符串" << value.toString();
}
else if (value.isDouble())
{
qDebug() << "数值型" << value.toDouble();
}
else if (value.isBool())
{
qDebug() << "布尔型" << value.toBool();
}
else if (value.isObject())
{
QJsonObject subObj = value.toObject();
QStringList subKeys = subObj.keys();
for(auto subKey : subKeys){
qDebug() << subObj.value(subKey);
}
}
else if (value.isArray())
{
qDebug() << "数组";
QJsonArray arr = value.toArray();
for (int j = 0; j < arr.size();j++)
{
qDebug() << arr[j].toString();
}
qDebug() << "数组结束";
}
}
3. 创建 JSON
创建 json 文档所需的类:
-
QJsonDocument:
- toJson(); 将 json 对象转为 json 字符串
-
QJsonObject: Json 对象类
- insert(key, value); 向节点中添加子节点
-
QJsonArray: Json 数组类
- append(); 向数组中追加单元
示例:
cpp
// 创建根节点
QJsonObject rootObj;
// 向根节点中添加节点
rootObj.insert("name", "韩梅梅");
rootObj.insert("age", 8);
// 创建数组类型节点
QJsonArray hobbies;
// 向节点中追加数据
hobbies.append("LOL");
hobbies.append("打篮球");
// 将节点添加到上级节点中
rootObj.insert("hobbies", hobbies);
// 创建对象类型节点
QJsonObject addr;
// 向节点中添加数据
addr.insert("city", "西安市");
addr.insert("area", "未央区");
// 将节点添加到上级节点中
rootObj.insert("addr", addr);
// 将根节点转换为 json 文档对象
QJsonDocument jsonDoc(rootObj);
// 将json文档对象转换为字符串
QByteArray jsonStr = jsonDoc.toJson(QJsonDocument::Indented);
// 将字符串写入 .json 文件中
QFile file("d:/a.json");
if (!file.open(QIODevice::WriteOnly))
{
qDebug() << "打开文件失败";
return;
}
file.write(jsonStr);
file.close();
4. XML概述
-
XML(Extensible Markup Language,可扩展标记语言),是一种类似于HTML的标记语言
-
html 都是系统预定义好的标签
-
xml 都是自定义的标签
-
-
XML是用来传输数据、保存数据,而不是显示数据。
-
XML的标签没有被预定义,用户需要在使用时自行进行定义。
-
XML是W3C(万维网联盟)的推荐标准。XML使用的树形结构更能表现出数据的包含关系
Qt 提供了两种方案来处理 XML 文档:
-
DOM 方式
-
QXmlStreamReader 和 QXmlStreamWriter
重点:要在项目中操作 xml 文档,需要在 .pro 文档中加入 xml 模块
QT += core gui xml
-
html: 超文本标记语言 (标签) 系统提前定义好的,每个标签都有不同的功能
-
xml:可扩展标记语言 (标签) 程序员自定义的标签,因为系统不认识自定义标签,所以不用来显示,而是用来保存数据的
5. DOM 方式
-
DOM(Document Object Model,即文档对象模型)
-
DOM 方式是将 XML 文档转换成应用程序可以遍历的树形结构,就可以访问其中的节点
-
缺点:将整个XML文档读入内存,消耗内存较多
5.1 载入 XML
-
使用 DOM 方式操作 XML,首先需要将 xml 文档载入,并保存到内存中
-
使用 QDomDocument 配合 QFile 进行文件读取即可
cpp
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
// 1. 通过文件操作,将xml文档的内容读取出来并保存到内存中
QDomDocument doc;
// 加载 xml 文档,并以只读方式打开
QFile file("./files/books.xml");
if (!file.open(QIODevice::ReadOnly))
{
qDebug() << "读取XML文档失败";
return;
}
// 将 XML 文档中的内容读取出来并以树形结构保存到 doc 中
if (!doc.setContent(&file))
{
qDebug() << "载入xml文档失败";
file.close();
return;
}
// 因为xml文档内容已经读取到内存当中了,所以可以关闭文件操作
file.close();
}
5.2 读取 xml 头部
目标: 获取 <?xml version="1.0" encoding="utf-8"?>
中的信息
-
xml 文档中的每个标签、属性、内容都称为节点
-
节点的基类为 QDomNode
-
标签又叫做 标签节点(元素节点) 【QDomElement】
-
属性又叫做属性节点 【QDomAttr】 (attribute)
-
内容又叫做内容节点 【QDomText】
-
-
重要方法:
-
nodeName() : 获取节点名称
-
nodeValue() : 获取节点值
-
nodeType() : 获取节点类型
-
cpp
QDomNode firstNode = doc.firstChild();
qDebug() << firstNode.nodeName();
5.3 读取根节点
-
xml 一般都有一个根节点, 在 books.xml 中,根节点为 library
-
读取根节点的方法为 : doc.documentElement()
- QDomElement 对象的 tagName 也能获取节点名称
cpp
QDomElement rootElement = doc.documentElement();
qDebug() << rootElement.tagName()
<< rootElement.nodeName()
<< rootElement.nodeValue()
<< rootElement.nodeType();
5.4 读取根节点下的子标签节点
核心方法:
-
isElement() : 判断是否为标签节点(元素节点)
-
nextSiblingElement() :获取下一个兄弟元素节点
-
toElement() : 将一个 QDomNode 节点转换为 QDomElement 节点。 (toAttr, toText)
-
qPrintable() : 使用该方法打印字符串时,没有引号
cpp
// 读取根节点
QDomElement rootElement = doc.documentElement();
// 获取根节点下的第一个子节点
QDomNode node = rootElement.firstChild();
// 遍历子节点
while (node.isElement())
{
// 将子节点转为元素节点
QDomElement e = node.toElement();
// 打印节点名称 和 id 属性值
qDebug() << qPrintable(e.nodeName()) << e.nodeName() << e.attribute("id");
// 找到下一个兄弟节点,并替换到 node 中,方便循环
node = node.nextSiblingElement();
}
5.5 遍历深层子节点
childNodes() : 该方法能够获取一个节点下所有的子节点,并返回一个 QDomNodeList 对象
cpp
// 读取根节点
QDomElement rootElement = doc.documentElement();
QDomNode node = rootElement.firstChild();
// 遍历子节点
while (node.isElement())
{
QDomElement e = node.toElement();
// 获取当前节点下所有的子节点
QDomNodeList list = e.childNodes();
// 遍历 list
for (int i = 0; i < list.size(); i++)
{
// 每得到一个节点,就将其转换为 DomElement 节点,通过 text 方法能够得到它的内容
QDomNode tmpNode = list.at(i);
QDomElement tmpEle = tmpNode.toElement();
qDebug() << tmpEle.nodeName() << tmpEle.text();
}
node = node.nextSiblingElement();
}
5.6 获取同名节点
elementsByTagName("标签名") : 该方法能够一次性获取 xml 文档中所有的同名元素节点
cpp
QDomNodeList list = doc.elementsByTagName("name");
for (int i = 0; i < list.size(); i++)
{
QDomNode node = list.at(i);
QDomElement ele = node.toElement();
qDebug() << ele.tagName() << ele.text();
}
5.7 创建 xml 文档
核心思路: (内存中操作,最后写文件)
-
创建一个 QDOMDocument 对象 (doc),该对象保存在内存中
-
向 doc 对象中添加节点
-
将 doc 对象写入一个 xml 文档中
cpp
QDomDocument doc;
// 创建头部 并将 头部添加到 doc 中
QDomProcessingInstruction head = doc.createProcessingInstruction("xml", "verison='1.0' encoding='utf-8'");
doc.appendChild(head);
// 创建根节点
QDomElement bookEle = doc.createElement("book");
bookEle.setAttribute("id", "01");
// 将book标签添加到文档中
doc.appendChild(bookEle);
// 创建name节点,加入到 book 节点中
QDomElement nameEle = doc.createElement("name");
QDomText t = doc.createTextNode("盗墓笔记");
nameEle.appendChild(t);
bookEle.appendChild(nameEle);
// 创建 author 节点,加入到 book 节点中
QDomElement authorEle = doc.createElement("author");
t = doc.createTextNode("南派三叔");
authorEle.appendChild(t);
bookEle.appendChild(authorEle);
// 使用 QTextStream 流来写文件
QFile file("./files/test.xml");
file.open(QIODevice::WriteOnly);
QTextStream out(&file);
// 参数1: 流对象
// 参数2: 子节点缩进
doc.save(out, 4);
6. 流方式(了解)
QXmlStreamReader : 使用流方式读取 XML 文档内容
QXmlStreamWriter : 使用流方式写 XML 文档
6.1 读取xml
执行逻辑: 流读取器(QXmlStreamReader )就是将XML文档报告为一个记号(tokens)流,通过循环从读取器中一个接一个的拉出记号。通过区分记号的类型,来实现 XML 文档的读取。
核心方法:
-
readNext(): 从xml输入流中读取下一个记号。
-
name(): 记号的名称,即<名称></名称>
-
isStartElement():判断当前已读取的记号是否为开始元素,开始元素即<>
-
isEndElement():判断当前已读取的记号是否为结束元素,结束元素即</>
-
readElementText():读取当前记号对应的文本值,<>文本值</>
-
atEnd():判断是否为文件结尾。
cpp
// 以只读方式打开 xml 文件
QFile file("./files/books.xml");
if(!file.open(QIODevice::ReadOnly))
{
qDebug() << "读取xml文件失败";
}
// 读取文件内容 QXmlStreamReader
// reader对象中保存了所有的记号
reader.setDevice(&file);
// 使用循环方式来依次获取所有的记号
while (!reader.atEnd())
{
// 获取本次循环得到的记号
QXmlStreamReader::TokenType type = reader.readNext();
// 区分记号
// 判断当前记号是否为 xml的头部
if (type == QXmlStreamReader::StartDocument)
{
qDebug() << reader.documentVersion() << reader.documentEncoding();
}
// 判断当前记号是否为 开始标签
if (type == QXmlStreamReader::StartElement)
{
qDebug() << '<' << reader.name() << '>'; // <user>
if (reader.attributes().hasAttribute("id"))
{
qDebug() << reader.attributes().value("id");
}
}
if (type == QXmlStreamReader::EndElement)
{
qDebug() << "</" << reader.name() << '>'; // </user>
}
if (type == QXmlStreamReader::Characters && !reader.isWhitespace())
{
qDebug() << reader.text();
}
}
file.close();
6.2 写XML
QXmlStreamWriter : 使用流方式写 XML 文档
常用方法:
-
writeStartDocument():写文档头,作用类似于创建一个xml文档,并在文档开头部分写入版本信息和编码信息默认为:
<?xml version="1.0" encoding="UTF-8"?>
-
writeEndDocument(): 对应于writeStartDocument(),当调用这个函数时,即表示文档信息写入完毕。
-
writeStartElement(): 写入开始记号 比如
<name>
-
writeEndElement(): 写入结束记号 比如
</name>
-
writeCharacters(): 向已有的标签中写入内容
-
writeTextElement(): 写入标签的同时写入标签的内容
cpp
QFile file("./files/test2.xml");
if(!file.open(QIODevice::WriteOnly)
{
qDebug() << "打开文件失败";
return;
}
QXmlStreamWriter write(&file);
// 设置是否需要格式化
write.setAutoFormatting(true);
// 写头部 xml 标签
write.writeStartDocument();
// 写开始标签
write.writeStartElement("book");
// 给开始标签设置属性
write.writeAttribute("title", "哈哈哈");
// 给标签book标签内些内容
write.writeTextElement("name", "张三三");
// 标签结束
write.writeEndElement();
// 整个文档结束
write.writeEndDocument();
// 关闭文件
file.close();