Введение
Обратная задача к чтению JSON (статья 222) — формирование и сохранение JSON-данных, например для записи конфигурации приложения или подготовки данных для отправки на сервер. В статье разберём, как собрать QJsonObject с нужными полями, вложенными объектами и массивами, и сохранить результат в файл.
Концепция
Построение JSON в Qt происходит в обратном порядке относительно чтения: создаётся QJsonObject, в него добавляются поля через оператор [] (например, obj["name"] = "значение"), для вложенных структур создаются отдельные QJsonObject/QJsonArray и присваиваются как значения полей родительского объекта. Когда объект полностью собран, он оборачивается в QJsonDocument и сериализуется в QByteArray через toJson(), который затем записывается в файл обычным QFile.
Пример кода: Запись JSON в Qt
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
void writeJsonToFile()
{
QJsonObject rootObject;
// Простые поля добавляются как обычное присваивание
rootObject["appName"] = "MyApp";
rootObject["maxUsers"] = 100;
rootObject["debugMode"] = true;
// Вложенный объект собирается отдельно, затем присваивается как значение поля
QJsonObject serverObject;
serverObject["host"] = "192.168.1.10";
serverObject["port"] = 8080;
rootObject["server"] = serverObject;
// Массив строк
QJsonArray usersArray;
usersArray.append("alice");
usersArray.append("bob");
usersArray.append("charlie");
rootObject["allowedUsers"] = usersArray;
// Превращаем объект в документ и сериализуем в байты
const QJsonDocument doc(rootObject);
// QJsonDocument::Indented — красивое, читаемое форматирование с отступами;
// QJsonDocument::Compact — компактный вариант без лишних пробелов
const QByteArray jsonBytes = doc.toJson(QJsonDocument::Indented);
QFile file("config.json");
if (!file.open(QIODevice::WriteOnly)) {
qDebug() << "Не удалось открыть файл для записи";
return;
}
file.write(jsonBytes);
file.close();
qDebug() << "JSON успешно записан в файл";
}
// Запись массива объектов (часто нужно для списков записей)
void writeArrayOfObjects()
{
QJsonArray productsArray;
struct Product { QString name; double price; };
const QList<Product> products = {
{"Клавиатура", 49.99},
{"Мышь", 19.99}
};
for (const Product &product : products) {
QJsonObject productObject;
productObject["name"] = product.name;
productObject["price"] = product.price;
productsArray.append(productObject);
}
QJsonDocument doc(productsArray); // корень документа — массив, а не объект
qDebug() << doc.toJson(QJsonDocument::Compact);
}
Пояснения к коду
QJsonObject поддерживает удобный синтаксис присваивания через оператор [], похожий на работу с обычным словарём — rootObject["appName"] = "MyApp" автоматически конвертирует строку в QJsonValue нужного типа. Для вложенных структур (как serverObject) нужно сначала полностью собрать дочерний объект отдельно, а затем присвоить его целиком как значение поля родительского объекта — присваивание происходит по значению, то есть последующие изменения serverObject уже не повлияют на то, что было записано в rootObject.
QJsonDocument::Indented форматирует результат с отступами и переносами строк, удобными для чтения человеком (например, при ручном редактировании конфигурационного файла), а QJsonDocument::Compact убирает все лишние пробелы, что предпочтительнее для передачи данных по сети, где размер сообщения важен. writeArrayOfObjects() показывает, что корнем документа может быть не только объект, но и массив — это нужно, например, когда сохраняется список записей без единого «обёртывающего» объекта.
Подводные камни
- Изменение вложенного
QJsonObjectпосле того, как он уже был присвоен родительскому объекту, в ожидании, что изменения отразятся в родителе. Присваивание копирует значение на момент присваивания — если нужно изменить вложенный объект, изменения нужно внести до присваивания, либо переприсвоить обновлённую версию заново после изменений. - Использование
QJsonDocument::Compactтам, где файл предполагается редактировать вручную человеком, или, наоборот,Indentedдля передачи по сети, где лишние пробелы и переносы увеличивают объём передаваемых данных без всякой пользы для машинного чтения. - Забытая проверка успешности открытия файла перед записью — аналогично статье про
QFile, без проверкиopen()записанные данные могут просто не сохраниться, если файл недоступен для записи, а программа продолжит работу, считая, что всё прошло успешно. - Попытка сохранить в JSON типы данных, не поддерживаемые форматом напрямую (например,
QDateTimeбез явного преобразования в строку или числовой timestamp) — JSON поддерживает только строки, числа, булевы значения, объекты, массивы иnull; любые другие типы Qt нужно явно конвертировать в одно из этих представлений перед записью вQJsonObject.