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

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

Введение

JSON — самый распространённый формат для хранения структурированных данных и обмена ими между приложениями. Если вам нужно прочитать JSON-файл или ответ от сервера в Qt, для этого есть набор классов QJsonDocument, QJsonObject, QJsonArray, QJsonValue — в этой статье разберём базовый процесс разбора JSON шаг за шагом.

Концепция

Процесс чтения JSON в Qt состоит из трёх шагов: (1) получить сырые байты JSON-текста — из файла, из сети или из строки; (2) распарсить их в QJsonDocument через QJsonDocument::fromJson(); (3) обратиться к содержимому документа как к объекту (QJsonObject, если корень JSON — {...}) или массиву (QJsonArray, если корень — [...]), и далее читать конкретные поля через .value("имя") или индекс [i].

Пример кода: Чтение JSON в Qt

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

void readJsonFromFile()
{
    QFile file("config.json");
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug() << "Не удалось открыть файл";
        return;
    }

    const QByteArray rawData = file.readAll();
    file.close();

    QJsonParseError parseError;
    const QJsonDocument doc = QJsonDocument::fromJson(rawData, &parseError);

    if (parseError.error != QJsonParseError::NoError) {
        qDebug() << "Ошибка разбора JSON:" << parseError.errorString();
        return;
    }

    if (!doc.isObject()) {
        qDebug() << "Ожидался JSON-объект в корне файла";
        return;
    }

    const QJsonObject obj = doc.object();

    // Чтение простых полей
    const QString appName = obj.value("appName").toString();
    const int maxUsers = obj.value("maxUsers").toInt();
    const bool debugMode = obj.value("debugMode").toBool(false); // false — значение по умолчанию

    qDebug() << "Название приложения:" << appName;
    qDebug() << "Максимум пользователей:" << maxUsers;
    qDebug() << "Режим отладки:" << debugMode;

    // Чтение вложенного объекта
    if (obj.contains("server") && obj.value("server").isObject()) {
        const QJsonObject serverObj = obj.value("server").toObject();
        qDebug() << "Адрес сервера:" << serverObj.value("host").toString();
        qDebug() << "Порт сервера:" << serverObj.value("port").toInt();
    }

    // Чтение массива
    if (obj.contains("allowedUsers") && obj.value("allowedUsers").isArray()) {
        const QJsonArray usersArray = obj.value("allowedUsers").toArray();
        for (const QJsonValue &userValue : usersArray) {
            qDebug() << "Разрешённый пользователь:" << userValue.toString();
        }
    }
}
{
    "appName": "MyApp",
    "maxUsers": 100,
    "debugMode": true,
    "server": {
        "host": "192.168.1.10",
        "port": 8080
    },
    "allowedUsers": ["alice", "bob", "charlie"]
}

Пояснения к коду

QJsonDocument::fromJson() принимает QByteArray и необязательный второй аргумент QJsonParseError, в который записывается информация об ошибке, если текст не является корректным JSON — проверка parseError.error != QJsonParseError::NoError обязательна, чтобы не продолжить работу с пустым или некорректным документом.

Метод .value("ключ") возвращает QJsonValue, который затем приводится к конкретному типу через .toString(), .toInt(), .toBool() и так далее — если ключ отсутствует в JSON, эти методы вернут «пустое» значение по умолчанию (пустую строку, 0, false), не выбрасывая исключение. Для toBool()/toInt() можно явно указать собственное значение по умолчанию вторым параметром, как показано с debugMode. Вложенные объекты читаются через .toObject(), а массивы — через .toArray() с последующим перебором в цикле for.

Подводные камни

  • Отсутствие проверки parseError перед использованием документа. Если JSON содержит синтаксическую ошибку (пропущенная запятая, незакрытая скобка), fromJson() вернёт пустой документ без явного исключения — без проверки parseError программа продолжит работу с пустыми данными, что трудно сразу диагностировать.
  • Обращение к полю без проверки contains(), когда поле может отсутствовать. Хотя .value() не крашится на отсутствующем ключе, для вложенных объектов и массивов лучше явно проверять contains() и isObject()/isArray() перед попыткой использовать значение как объект или массив — иначе .toObject() на отсутствующем или неверном по типу значении вернёт пустой объект, и дальнейший код может работать с «пустыми» данными без явного понимания причины.
  • Путаница между объектом ({...}) и массивом ([...]) в корне JSON. Если файл начинается с [, а код пытается вызвать doc.object(), результат будет некорректным — нужно сначала проверить doc.isObject()/doc.isArray(), как показано в примере.
  • Чтение чисел с потерей точности для очень больших значений. QJsonValue::toInt() и toDouble() имеют свои ограничения по диапазону — для очень больших целых чисел (превышающих диапазон int) или чисел, требующих повышенной точности, нужно использовать toDouble() с пониманием его ограничений или хранить такие значения как строки.