Различные статьи по Qt и c++

Запись JSON в Qt: пошаговое руководство для начинающих

Введение

Обратная задача к чтению 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.