Json и Qt

Запись в JSON в Qt: QJsonObject, QJsonArray и сохранение в файл

обновлено 4 391 просмотров

Введение

JSON — удобный формат для хранения настроек, экспорта данных и обмена с веб-сервисами. О самом формате мы рассказывали в статье JSON: назначение, структура и особенности формата. Здесь разберём, как выполнить запись в JSON в Qt: создать объекты и массивы, собрать из них документ и сохранить его в файл. Все примеры используют стандартные классы модуля QtCore, дополнительные библиотеки не нужны.

Классы для записи JSON в Qt

  • QJsonObject — объект с парами «ключ — значение»;
  • QJsonArray — массив значений;
  • QJsonValue — одно значение: строка, число, bool, null, объект или массив;
  • QJsonDocument — документ, который превращается в текст методом toJson().

Простая запись в JSON: первый пример

Создадим объект с разными типами значений и массивом, а затем сохраним его в файл:

#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QFile>

QJsonObject recordObject;
recordObject["Test1"] = "1";
recordObject["Test2"] = 123.4;
recordObject["Test3"] = 43;
recordObject["Test4"] = true;
recordObject["Test5"] = QString("1,2,3,4,5");

QJsonArray numbersArray;
numbersArray.append("1");
numbersArray.append("2");
numbersArray.append("3");
recordObject["Test6"] = numbersArray;

QJsonDocument doc(recordObject);

QFile file("test1.json");
if (file.open(QIODevice::WriteOnly)) {
    file.write(doc.toJson(QJsonDocument::Indented));
    file.close();
}

Значения можно добавлять через оператор [] или метод insert(). Преобразование через QJsonValue::fromVariant() не требуется: QJsonValue сам создаётся из строк, чисел и bool. Метод toJson() возвращает QByteArray в кодировке UTF-8, поэтому его удобно сразу записать в файл методом write().

На выходе получится файл test1.json:

{
    "Test1": "1",
    "Test2": 123.4,
    "Test3": 43,
    "Test4": true,
    "Test5": "1,2,3,4,5",
    "Test6": [
        "1",
        "2",
        "3"
    ]
}

Вложенные объекты

Объекты можно вкладывать друг в друга. Создадим более сложный файл:

QJsonObject obj2Object;
obj2Object["Test4"] = "4";
obj2Object["Test5"] = "5";
obj2Object["Test6"] = "6";
obj2Object["Test7"] = "1";
obj2Object["Test8"] = 123.4;
obj2Object["Test9"] = 43;
obj2Object["Test10"] = true;

QJsonObject objObject;
objObject["Test1"] = "1";
objObject["Test2"] = "2";
objObject["Test3"] = "3";
objObject["Test11"] = obj2Object;

QJsonObject recordObject;
recordObject["Test12"] = objObject;

QFile file("test2.json");
if (file.open(QIODevice::WriteOnly))
    file.write(QJsonDocument(recordObject).toJson());

Содержимое файла test2.json:

{
    "Test12": {
        "Test1": "1",
        "Test11": {
            "Test10": true,
            "Test4": "4",
            "Test5": "5",
            "Test6": "6",
            "Test7": "1",
            "Test8": 123.4,
            "Test9": 43
        },
        "Test2": "2",
        "Test3": "3"
    }
}

Обратите внимание на порядок ключей. QJsonObject хранит ключи отсортированными по алфавиту, поэтому «Test10» и «Test11» оказались раньше «Test2». Порядок, в котором вы добавляли значения, не сохраняется. Если порядок важен, храните данные в массиве.

Практический пример: сохранение настроек

Более жизненный пример — сохранение настроек окна и списка последних файлов:

void saveSettings(const QString &path, const QSize &size,
                  bool maximized, const QStringList &recent)
{
    QJsonObject window;
    window["width"] = size.width();
    window["height"] = size.height();
    window["maximized"] = maximized;

    QJsonObject root;
    root["version"] = 1;
    root["window"] = window;
    root["recentFiles"] = QJsonArray::fromStringList(recent);

    QSaveFile file(path);
    if (file.open(QIODevice::WriteOnly)) {
        file.write(QJsonDocument(root).toJson());
        file.commit();
    }
}

Класс QSaveFile записывает данные во временный файл и заменяет оригинал только после успешного commit(). Так файл настроек не повредится, если программа завершится во время записи.

Запись массива объектов

Список структур удобно сохранять как массив объектов:

struct User { QString name; int age; bool admin; };

QJsonArray usersArray;
for (const User &u : users) {
    QJsonObject obj;
    obj["name"] = u.name;
    obj["age"] = u.age;
    obj["admin"] = u.admin;
    usersArray.append(obj);
}

QJsonObject root;
root["users"] = usersArray;

Форматирование: Indented и Compact

Метод toJson() принимает формат вывода. QJsonDocument::Indented (по умолчанию) добавляет отступы и переносы строк — такой файл удобно читать и править вручную. QJsonDocument::Compact записывает всё в одну строку без пробелов. Это экономит место и подходит для передачи данных по сети.

QByteArray pretty  = doc.toJson(QJsonDocument::Indented);
QByteArray compact = doc.toJson(QJsonDocument::Compact);

Проверка: чтение записанного файла

Чтобы убедиться, что данные записаны правильно, прочитайте файл обратно:

QFile file("test1.json");
if (file.open(QIODevice::ReadOnly)) {
    QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
    qDebug() << doc["Test2"].toDouble();   // 123.4
}

Подробнее о чтении и разборе JSON смотрите в статье Работа с JSON в Qt C++.

Заключение

Сохранение данных в формате JSON сводится к трём шагам: собрать данные в QJsonObject и QJsonArray, обернуть их в QJsonDocument и сохранить результат toJson() в файл. Для важных данных используйте QSaveFile, а если порядок элементов имеет значение, храните их в массиве, а не в объекте.