Json и Qt

Работа с JSON в Qt C++: чтение и разбор QJsonDocument

обновлено 750 просмотров

Введение

Работа с JSON в Qt строится на нескольких классах модуля QtCore: QJsonDocument, QJsonObject, QJsonArray и QJsonValue. С их помощью можно разобрать ответ веб-сервиса, загрузить конфигурацию или сохранить состояние приложения. В этой статье основное внимание уделено чтению. Разбор JSON включает проверку ошибок и типов, доступ к вложенным данным, обход объектов и массивов и изменение документа. О создании и сохранении файлов подробно рассказано в статье Запись в JSON в Qt.

Основные классы

  • QJsonDocument — полный JSON-документ, умеет разбирать текст и превращать данные обратно в текст;
  • QJsonObject — объект из пар «ключ — значение»;
  • QJsonArray — массив значений;
  • QJsonValue — универсальный контейнер для любого значения JSON;
  • QJsonParseError — описание ошибки разбора.

Пример 1: чтение JSON из строки

QString jsonString = R"(
{
    "name": "Тестовая модель",
    "version": 1,
    "enabled": true,
    "parameters": [1.0, 2.5, 3.75]
}
)";

QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8());
if (!doc.isNull() && doc.isObject()) {
    QJsonObject obj = doc.object();
    QString name = obj["name"].toString();
    int version = obj["version"].toInt();
    bool enabled = obj["enabled"].toBool();
    QJsonArray params = obj["parameters"].toArray();

    qDebug() << "Имя:" << name;
    qDebug() << "Версия:" << version;
    qDebug() << "Включено:" << enabled;
    qDebug() << "Параметры:" << params;
}

Метод fromJson() принимает QByteArray в UTF-8. Если текст некорректен, возвращается пустой документ, и isNull() вернёт true. Корнем документа может быть как объект, так и массив, поэтому перед вызовом object() стоит проверить isObject().

Пример 2: чтение JSON из файла с проверкой ошибок

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

QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError);
if (parseError.error != QJsonParseError::NoError) {
    qWarning() << "Ошибка разбора JSON:" << parseError.errorString()
               << "в позиции" << parseError.offset;
    return;
}

QJsonObject obj = doc.object();

Поле offset показывает, в каком месте текста найдена ошибка. Это сильно упрощает поиск лишней запятой или пропущенной кавычки.

Разбор JSON: проверка ключей и типов

Если ключа нет, оператор [] вернёт значение типа Undefined, а методы toString(), toInt() вернут пустую строку или ноль. Чтобы не перепутать отсутствующее значение с настоящим нулём, проверяйте ключ и тип явно:

if (obj.contains("version") && obj["version"].isDouble()) {
    int version = obj["version"].toInt();
}

// Значение по умолчанию, если ключа нет
int timeout = obj["timeout"].toInt(30);
QString mode = obj.value("mode").toString("auto");

Для проверки типа есть методы isString(), isDouble(), isBool(), isArray(), isObject(), isNull() и isUndefined(). Все числа в JSON хранятся как double, поэтому для целых значений тоже используется isDouble().

Доступ к вложенным данным

// {"window": {"size": {"width": 800, "height": 600}}}
int width = obj["window"].toObject()["size"].toObject()["width"].toInt();

В Qt 6 цепочку можно записать короче, потому что QJsonValue тоже поддерживает оператор []:

int width = obj["window"]["size"]["width"].toInt();

Обход объектов и массивов

// Все ключи объекта
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it)
    qDebug() << it.key() << ":" << it.value();

// Элементы массива
QJsonArray users = obj["users"].toArray();
for (const QJsonValue &v : users) {
    QJsonObject user = v.toObject();
    qDebug() << user["name"].toString() << user["age"].toInt();
}

Изменение документа

Методы toObject() и toArray() возвращают копии. Поэтому изменённый вложенный объект нужно записать обратно:

QJsonObject window = obj["window"].toObject();
window["width"] = 1024;
obj["window"] = window;          // без этой строки изменение потеряется

doc.setObject(obj);

Преобразование в QVariant

Если удобнее работать со стандартными типами Qt, документ можно превратить в QVariantMap или QVariantList:

QVariantMap map = obj.toVariantMap();
QStringList files = map["recentFiles"].toStringList();

Пример 3: формирование JSON

Создание документа выглядит зеркально чтению:

QJsonObject obj;
obj["status"] = "ok";
obj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
obj["parameters"] = QJsonArray{4.0, 5.5, 6.75};

QJsonDocument doc(obj);
qDebug().noquote() << doc.toJson(QJsonDocument::Indented);

Заключение

Работа с JSON в Qt удобна и не требует сторонних библиотек. При разборе всегда передавайте QJsonParseError в fromJson(), проверяйте наличие ключей и типы значений, а изменённые вложенные объекты записывайте обратно в родителя. Так программа будет устойчиво обрабатывать даже некорректные или неполные данные. Описание самого формата смотрите в статье JSON: назначение, структура и особенности формата.