当前位置: 首页 > ds >正文

Qt---JSON处理体系

Qt框架提供了一套完整的JSON处理工具集,其中QJsonObject、QJsonArray、QJsonDocument和QJsonValue是核心组件。这些类共同构成了JSON数据的创建、解析、修改和序列化能力,广泛应用于网络数据交换、配置文件处理、数据持久化等场景。

一、Qt JSON体系的整体架构

Qt的JSON处理模块(Qt Core内置)采用分层设计,各组件职责明确:

  • QJsonValue:基础值类型容器,封装JSON支持的所有原始类型(字符串、数字、布尔等)及复合类型(对象、数组),是其他类交互的基础;
  • QJsonObject:对应JSON对象({}),管理无序键值对集合(键为字符串,值为QJsonValue);
  • QJsonArray:对应JSON数组([]),管理有序QJsonValue列表;
  • QJsonDocument:顶层文档容器,负责JSON数据的解析(从字符串/二进制到对象/数组)和序列化(从对象/数组到字符串/二进制)。

四者的关系可概括为:QJsonDocument包含QJsonObject或QJsonArray,后两者由QJsonValue组成,而QJsonValue可嵌套其他复合类型,形成任意深度的JSON结构。

二、QJsonValue:JSON值的基础容器

QJsonValue是Qt JSON体系的"原子",它封装了JSON规范定义的所有值类型,是QJsonObject和QJsonArray的基本组成单元。

1. 支持的类型与类型判断

QJsonValue支持7种JSON值类型,通过Type枚举定义:

  • Null:空值(对应JSON的null);
  • Bool:布尔值(true/false);
  • Double:数值(JSON不区分整数和浮点数,统一用double存储);
  • String:字符串(UTF-8编码);
  • Array:数组(嵌套QJsonArray);
  • Object:对象(嵌套QJsonObject);
  • Undefined:未定义(仅用于错误场景)。

通过类型判断方法可检查当前值的类型:

QJsonValue value = ...;
if (value.isNull())        { /* 处理null */ }
else if (value.isBool())   { /* 处理布尔值 */ }
else if (value.isDouble()) { /* 处理数值 */ }
else if (value.isString()) { /* 处理字符串 */ }
else if (value.isArray())  { /* 处理数组 */ }
else if (value.isObject()) { /* 处理对象 */ }
2. 值的获取与转换

QJsonValue提供类型安全的取值方法,若类型不匹配,返回默认值(如toInt()对非数字值返回0):

// 布尔值
bool b = value.toBool();                  // 类型不匹配返回false
bool bOk;
bool b2 = value.toBool(&bOk);             // bOk指示转换是否成功// 数值(自动转换为对应类型)
double d = value.toDouble();              // 基础数值获取
int i = value.toInt();                    // 截断为整数
qint64 ll = value.toInteger();            // Qt 5.14+,支持大整数// 字符串
QString s = value.toString();             // 非字符串类型返回空字符串
QString s2 = value.toString("default");   // 转换失败时返回默认值// 复合类型(返回引用,需先判断类型)
if (value.isArray()) {QJsonArray arr = value.toArray();     // 转换为数组
}
if (value.isObject()) {QJsonObject obj = value.toObject();   // 转换为对象
}

注意:JSON数值在Qt中统一以double存储,对于超出double精度的大整数(如超过53位的整数),可能导致精度丢失。Qt 5.14+新增toInteger()方法,通过内部判断处理大整数场景。

3. 构造与修改

QJsonValue支持隐式构造,可直接用原始类型初始化:

QJsonValue v1;                          // 默认构造为Null
QJsonValue v2(true);                    // Bool类型
QJsonValue v3(3.14);                    // Double类型
QJsonValue v4("hello");                 // String类型
QJsonValue v5(QJsonArray{1, 2, 3});     // Array类型
QJsonValue v6(QJsonObject{{"key", "val"}}); // Object类型

通过setValue()可修改已有QJsonValue的值:

QJsonValue value;
value.setValue(42);          // 改为Double类型
value.setValue("new str");   // 改为String类型

三、QJsonObject:JSON对象的键值对管理

QJsonObject对应JSON规范中的对象({key:value, ...}),是无序键值对的集合,键为唯一字符串,值为QJsonValue。

1. 构造与初始化

QJsonObject可通过多种方式创建:

// 1. 空对象构造
QJsonObject obj1;// 2. 初始化列表构造(C++11+)
QJsonObject obj2{{"name", "Qt"},{"version", 6.5},{"stable", true}
};// 3. 从QVariantMap转换(键为QString,值为QVariant支持的类型)
QVariantMap varMap;
varMap["id"] = 1001;
varMap["active"] = true;
QJsonObject obj3 = QJsonObject::fromVariantMap(varMap);
2. 键值对的添加、修改与删除

QJsonObject提供丰富的接口管理键值对:

QJsonObject obj;// 添加/修改键值对(operator[])
obj["name"] = "Qt JSON";               // 新增字符串键值对
obj["version"] = 6.5;                  // 新增数值键值对
obj["features"] = QJsonArray{"fast", "safe"}; // 新增数组值// 插入键值对(insert(),返回迭代器)
auto it = obj.insert("stable", true);  // 插入布尔值// 删除键值对
obj.remove("version");                 // 通过键删除
obj.erase(it);                         // 通过迭代器删除// 检查键是否存在
bool hasName = obj.contains("name");   // true

注意:键是唯一的,重复插入相同键会覆盖原有值;键的比较是大小写敏感的("Name""name"视为不同键)。

3. 键与值的访问

访问键值对的常用方法:

// 获取所有键(返回QStringList,无序)
QStringList keys = obj.keys();// 通过键获取值(value()返回QJsonValue,不存在则返回Null)
QJsonValue nameVal = obj.value("name");
QJsonValue versionVal = obj["version"]; // operator[]等价于value()// 安全获取嵌套值(避免链式调用中的空指针)
QJsonValue nestedVal = obj["parent"]["child"]; // 若parent不存在,返回Null// 获取值的原始类型(需先判断类型)
if (nameVal.isString()) {QString name = nameVal.toString();
}
4. 遍历与迭代

QJsonObject支持STL风格和Java风格两种迭代方式:

// 1. STL风格迭代(只读)
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {qDebug() << "键:" << it.key() << ",值:" << it.value();
}// 2. STL风格迭代(可修改)
for (auto it = obj.begin(); it != obj.end(); ++it) {if (it.key() == "version") {it.value() = 6.6; // 修改值}
}// 3. Java风格迭代(QJsonObjectIterator)
QJsonObjectIterator it(obj);
while (it.hasNext()) {it.next();qDebug() << it.key() << ":" << it.value();
}
5. 与QVariantMap的转换

QJsonObject与QVariantMap(Qt的通用键值对容器)可双向转换,便于与Qt其他模块(如QSettings)交互:

// QJsonObject → QVariantMap
QVariantMap varMap = obj.toVariantMap();// QVariantMap → QJsonObject(仅支持QVariant可转换为QJsonValue的类型)
QJsonObject objFromMap = QJsonObject::fromVariantMap(varMap);

转换限制:QVariant中的部分类型(如QColor、QDateTime)无法直接转换为JSON值,需手动序列化(如转为字符串)后再存储。

四、QJsonArray:JSON数组的有序列表管理

QJsonArray对应JSON规范中的数组([value1, value2, ...]),是有序QJsonValue的列表,支持重复元素和嵌套结构。

1. 构造与初始化

QJsonArray的创建方式与QJsonObject类似:

// 1. 空数组构造
QJsonArray arr1;// 2. 初始化列表构造
QJsonArray arr2{1, 2, 3, 4};// 3. 从QVariantList转换
QVariantList varList{"a", "b", "c"};
QJsonArray arr3 = QJsonArray::fromVariantList(varList);// 4. 嵌套构造(数组包含对象)
QJsonArray arr4{QJsonObject{{"id", 1}, {"name", "item1"}},QJsonObject{{"id", 2}, {"name", "item2"}}
};
2. 元素的添加、插入与删除

QJsonArray提供索引化操作接口,支持动态调整元素:

QJsonArray arr;// 尾部添加元素
arr.append(10);                  // 添加数值
arr.append("text");              // 添加字符串
arr.append(QJsonObject{{"k", "v"}}); // 添加对象// 指定位置插入元素
arr.insert(1, true);             // 在索引1处插入布尔值// 删除元素
arr.removeAt(0);                 // 删除索引0的元素
arr.pop_back();                  // 删除最后一个元素(Qt 5.10+)
arr.pop_front();                 // 删除第一个元素(Qt 5.10+)
3. 元素访问与修改

通过索引访问元素,支持读写操作:

// 获取元素(at()返回const引用,operator[]返回可修改引用)
QJsonValue first = arr.at(0);    // 只读访问,越界返回Null
QJsonValue second = arr[1];      // 可修改访问// 修改元素
arr[0] = 20;                     // 直接赋值修改// 获取数组大小
int size = arr.size();
bool isEmpty = arr.isEmpty();// 安全访问(避免越界)
if (index >= 0 && index < arr.size()) {QJsonValue val = arr[index];
}
4. 遍历与迭代

QJsonArray支持索引遍历和迭代器遍历:

// 1. 索引遍历
for (int i = 0; i < arr.size(); ++i) {QJsonValue val = arr[i];// 处理元素
}// 2. STL风格迭代(只读)
for (auto it = arr.constBegin(); it != arr.constEnd(); ++it) {qDebug() << *it;
}// 3. STL风格迭代(可修改)
for (auto it = arr.begin(); it != arr.end(); ++it) {if (it->isDouble()) {*it = it->toDouble() * 2; // 数值翻倍}
}
5. 与QVariantList的转换

类似QJsonObject,QJsonArray可与QVariantList双向转换:

// QJsonArray → QVariantList
QVariantList varList = arr.toVariantList();// QVariantList → QJsonArray
QJsonArray arrFromList = QJsonArray::fromVariantList(varList);

五、QJsonDocument:JSON文档的解析与序列化

QJsonDocument是JSON数据的顶层容器,负责JSON文本与Qt JSON对象/数组的相互转换,是数据输入输出的核心。

1. 构造与初始化

QJsonDocument可从QJsonObject或QJsonArray构造,代表一个完整的JSON文档:

// 从对象构造
QJsonObject obj{{"key", "value"}};
QJsonDocument doc1(obj);// 从数组构造
QJsonArray arr{1, 2, 3};
QJsonDocument doc2(arr);// 空文档(isNull()返回true)
QJsonDocument emptyDoc;
2. JSON解析(从文本到对象/数组)

通过fromJson()方法解析JSON文本(UTF-8编码),支持字符串或二进制数据:

// 待解析的JSON字符串(必须是UTF-8编码)
QByteArray jsonData = R"({"name":"Qt","version":6.5})";// 解析并处理错误
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);if (error.error != QJsonParseError::NoError) {qDebug() << "解析错误:" << error.errorString() << "位置:" << error.offset;return;
}// 判断文档类型(对象或数组)
if (doc.isObject()) {QJsonObject obj = doc.object(); // 转换为对象
} else if (doc.isArray()) {QJsonArray arr = doc.array();   // 转换为数组
}

解析注意事项

  • 输入必须是UTF-8编码的JSON文本,其他编码(如GBK)需先转换为UTF-8;
  • JSON文本必须是完整的对象或数组(不能是单独的字符串/数字);
  • 解析错误时,error对象会包含错误类型(如InvalidEscapeSequenceMismatchedBrace)和错误位置。
3. JSON序列化(从对象/数组到文本)

通过toJson()方法将文档转换为JSON文本,支持格式控制:

QJsonDocument doc = ...;// 1. 紧凑格式(无缩进,最小化输出)
QByteArray compactJson = doc.toJson(QJsonDocument::Compact);// 2. 缩进格式(便于阅读,默认4空格缩进)
QByteArray indentedJson = doc.toJson(QJsonDocument::Indented);// 输出示例:
// Compact: {"name":"Qt","version":6.5}
// Indented: {
//     "name": "Qt",
//     "version": 6.5
// }

序列化特性

  • 输出始终为UTF-8编码;
  • 特殊字符(如引号、控制字符)会自动转义;
  • 数值序列化会保留精度(如整数123不会显示为123.0)。
4. 与文件的交互

结合QFile可实现JSON文件的读写:

// 写入JSON文件
QJsonObject obj{{"data", "example"}};
QJsonDocument doc(obj);
QFile file("output.json");
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {file.write(doc.toJson(QJsonDocument::Indented));file.close();
}// 读取JSON文件
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {QByteArray data = file.readAll();file.close();QJsonDocument readDoc = QJsonDocument::fromJson(data);// 处理读取的文档
}

六、高级特性与最佳实践

1. 嵌套结构处理

Qt JSON类支持任意深度的嵌套(对象包含数组,数组包含对象等),需注意逐层访问以避免错误:

// 构建嵌套结构:对象包含数组,数组包含对象
QJsonObject root;
QJsonArray items;
items.append(QJsonObject{{"id", 1}, {"name", "item1"}});
items.append(QJsonObject{{"id", 2}, {"name", "item2"}});
root["items"] = items;
root["page"] = 1;// 解析嵌套结构
QJsonDocument doc(root);
QJsonObject parsedRoot = doc.object();
if (parsedRoot.contains("items") && parsedRoot["items"].isArray()) {QJsonArray parsedItems = parsedRoot["items"].toArray();for (const QJsonValue &itemVal : parsedItems) {if (itemVal.isObject()) {QJsonObject itemObj = itemVal.toObject();qDebug() << "ID:" << itemObj["id"].toInt();}}
}
2. 性能与内存考量
  • 隐式数据共享:QJsonObject、QJsonArray等类采用隐式数据共享(copy-on-write),复制操作开销低,修改时才真正复制数据;
  • 大型JSON处理:解析几MB以上的JSON时,建议使用QJsonDocument::fromJson()QByteArray重载,避免中间字符串转换;
  • 内存释放:复杂嵌套结构会占用较多内存,不再使用时需确保所有引用都被释放(尤其是迭代器和临时对象)。
3. 类型安全与错误处理
  • 始终先判断QJsonValue的类型,再调用对应的取值方法(如if (val.isString()) val.toString());
  • 解析JSON时必须检查QJsonParseError,避免因格式错误导致程序崩溃;
  • 处理网络获取的JSON数据时,需先验证数据结构(如必要的键是否存在),再进行解析。
4. 与网络模块的协同

在网络请求中(如QNetworkAccessManager),常需解析响应数据为JSON:

// 假设reply是QNetworkReply指针
connect(reply, &QNetworkReply::finished, [reply]() {if (reply->error() == QNetworkReply::NoError) {QByteArray data = reply->readAll();QJsonParseError error;QJsonDocument doc = QJsonDocument::fromJson(data, &error);if (error.error == QJsonParseError::NoError) {// 处理JSON文档}}reply->deleteLater();
});

七、常见问题与解决方案

  1. 中文乱码:JSON文本必须是UTF-8编码,若源数据为其他编码(如GBK),需用QTextCodec转换为UTF-8后再解析;
  2. 大整数精度丢失:JSON数值以double存储,超过53位的整数需以字符串形式存储,解析时手动转换为qint64
  3. 嵌套过深导致栈溢出:递归遍历深度超过1000层的JSON结构可能导致栈溢出,建议改用迭代方式遍历;
  4. 键名重复:QJsonObject允许通过insert()覆盖重复键,若需保留所有键(非标准JSON行为),需手动处理为数组。

Qt的QJsonObject、QJsonArray、QJsonDocument和QJsonValue共同构成了完整的JSON处理体系,其设计兼顾了易用性与灵活性。QJsonValue作为基础值容器,支撑了JSON类型系统;QJsonObject和QJsonArray分别管理键值对和有序列表,实现了复合结构;QJsonDocument则承担了数据解析与序列化的重任。

http://www.xdnf.cn/news/20371.html

相关文章:

  • LeetCode_位运算
  • 安卓学习 之 EditText 控件
  • C/C++中的可变参数 (Variadic Arguments)函数机制
  • Linux学习-硬件(串口通信)
  • 【Android】SQLite使用——增删查改
  • 有哪些AI产品可以真正提高办公和学习效率?
  • 【LeetCode】2749. 得到整数零需要执行的最少操作数
  • 关于无法导入父路径的问题
  • MySQL源码部署(rhel7)
  • SQL面试题及详细答案150道(61-80) --- 多表连接查询篇
  • java面试中经常会问到的集合问题有哪些(基础版)
  • GigaDevice(兆易创新)GD25Q64CSJGR 64Mbit FLASH
  • c#动态树形表达式详解
  • uni-app 和 uni-app x 的区别
  • 【Cell Systems】SpotGF空间转录组去噪算法文献分享
  • 图像去雾:从暗通道先验到可学习融合——一份可跑的 PyTorch 教程
  • <video> 标签基础用法
  • MySQL-安装MySQL
  • UE4 Mac构建编译报错 no template named “is_void_v” in namespace “std”
  • 无需bootloader,BootROM -> Linux Kernel 启动模式
  • Java全栈开发工程师面试实录:从基础到实战的深度探讨
  • PyTorch图像数据转换为张量(Tensor)并进行归一化的标准操作
  • 管理中心理学问:动机与管理的关联
  • 什么是CRM?定义、作用、功能、选型|CRM百科
  • 使用若依加Trae快速搭建一对儿多对多CRUD
  • 移植Qt4.8.7到ARM40-A5
  • PiscCode基于 Mediapipe 实现轨迹跟踪
  • TOGAF之架构标准规范-迁移计划
  • nginx 反向代理使用变量的坑
  • 亚马逊商品转化率怎么提高?从传统运营到智能广告的系统化突破