Qt- JSON&XML

发布于:2024-10-17 ⋅ 阅读:(10) ⋅ 点赞:(0)

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 中基础类型数据

// 读取 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 中所有数据

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(); 向数组中追加单元

示例:

// 创建根节点
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 进行文件读取即可

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() : 获取节点类型

QDomNode firstNode = doc.firstChild();
qDebug() << firstNode.nodeName();

5.3 读取根节点

  • xml 一般都有一个根节点, 在 books.xml 中,根节点为 library

  • 读取根节点的方法为 : doc.documentElement()

    • QDomElement 对象的 tagName 也能获取节点名称

QDomElement rootElement = doc.documentElement();
qDebug() << rootElement.tagName() 
  			 << rootElement.nodeName() 
  			 << rootElement.nodeValue() 
  			 << rootElement.nodeType();

5.4 读取根节点下的子标签节点

核心方法:

  • isElement() : 判断是否为标签节点(元素节点)

  • nextSiblingElement() :获取下一个兄弟元素节点

  • toElement() : 将一个 QDomNode 节点转换为 QDomElement 节点。 (toAttr, toText)

  • qPrintable() : 使用该方法打印字符串时,没有引号

// 读取根节点
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 对象

// 读取根节点
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 文档中所有的同名元素节点

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 文档

核心思路: (内存中操作,最后写文件)

  1. 创建一个 QDOMDocument 对象 (doc),该对象保存在内存中

  2. 向 doc 对象中添加节点

  3. 将 doc 对象写入一个 xml 文档中

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():判断是否为文件结尾。

// 以只读方式打开 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(): 写入标签的同时写入标签的内容

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();

 


网站公告

今日签到

点亮在社区的每一天
去签到