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
对象会包含错误类型(如InvalidEscapeSequence
、MismatchedBrace
)和错误位置。
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();
});
七、常见问题与解决方案
- 中文乱码:JSON文本必须是UTF-8编码,若源数据为其他编码(如GBK),需用
QTextCodec
转换为UTF-8后再解析; - 大整数精度丢失:JSON数值以double存储,超过53位的整数需以字符串形式存储,解析时手动转换为
qint64
; - 嵌套过深导致栈溢出:递归遍历深度超过1000层的JSON结构可能导致栈溢出,建议改用迭代方式遍历;
- 键名重复:QJsonObject允许通过
insert()
覆盖重复键,若需保留所有键(非标准JSON行为),需手动处理为数组。
Qt的QJsonObject、QJsonArray、QJsonDocument和QJsonValue共同构成了完整的JSON处理体系,其设计兼顾了易用性与灵活性。QJsonValue作为基础值容器,支撑了JSON类型系统;QJsonObject和QJsonArray分别管理键值对和有序列表,实现了复合结构;QJsonDocument则承担了数据解析与序列化的重任。