Evgenii Legotckoi
Evgenii Legotckoi26 октября 2018 г. 17:35

Qt/C++ - Урок 085. Работа с QJsonObject, QJsonArray, QJsonDocument. Сохранение и считывание JSON из файлов

Рассмотрим небольшой пример по формированию JSON документа из, например, текста и заголовка этого текста.

Например у текста есть:

  • Заголовок - First Title
  • Контент - First Content

И т.д.

Будем добавлять данный текст в QJsonObject , который будем добавлять в массив текстов QJsonArray . Массив объектов будет находиться в общем рабочем QJsonObject , который будем сохранять в файл.

Всё это будем делать через графический интерфейс, в котором есть:

  • QLineEdit - titleLineEdit - содержит заголовок текста, который нужно добавить
  • QTextEdit - contentTextEdit - содержит содержимое текста, который нужно добавить
  • QTextEdit - jsonDocumentTextEdit - предпросмотр JSON документа
  • QPushButton - addButton - кнопка для добавления нового текста в JSON
  • QPushButton - clearButton - кнопка для удаления всех текстов из  текущего QJsonObject
  • QPushButton - saveButton - кнопка для сохранения JSON документа в файл
  • QPushButton - loadButton - кнопка для считывания JSON из файла

В данном случае сможем считать JSON файл и добавить дополнительные тексты в считанный файл.

Приложение будет выглядеть так.


Реализация

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QJsonObject>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    // Слот добавления нового текста в массив текстов
    void onAddButtonClicked();
    // Слот удаления всей информации о текстах в текущем объекте
    void onClearButtonClicked();
    // Слот сохранения текстов в json файл
    void onSaveButtonClicked();
    // Слот загрузки текстов в программу из json файла
    void onLoadButtonClicked();

    Ui::Widget *ui;

    // Текущий json объект, с которым производится работа
    QJsonObject m_currentJsonObject;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

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

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    // Подключение слотов обработчиков к сигналам клика по кнопкам
    connect(ui->addButton, &QPushButton::clicked, this, &Widget::onAddButtonClicked);
    connect(ui->clearButton, &QPushButton::clicked, this, &Widget::onClearButtonClicked);
    connect(ui->saveButton, &QPushButton::clicked, this, &Widget::onSaveButtonClicked);
    connect(ui->loadButton, &QPushButton::clicked, this, &Widget::onLoadButtonClicked);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::onAddButtonClicked()
{
    // Создаём объект текста
    QJsonObject textObject;
    textObject["title"] = ui->titleLineEdit->text();                // Устанавливаем заголовок текста
    textObject["content"] = ui->contentTextEdit->toPlainText();     // Устанавливаем содержание текста
    QJsonArray textsArray = m_currentJsonObject["texts"].toArray(); // Забираем текущий массив текстов, даже если он не существует, он будет создан автоматически
    textsArray.append(textObject);                                  // Добавляем объект текста в массив
    m_currentJsonObject["texts"] = textsArray;                      // Сохраняем массив обратно в текущий объект

    // Устанавливаем текст всего Json объекта в текстовое поле для проверки
    ui->jsonDocumentTextEdit->setText(QJsonDocument(m_currentJsonObject).toJson(QJsonDocument::Indented));
}

void Widget::onClearButtonClicked()
{
    m_currentJsonObject = QJsonObject();    // Пересоздаём новый текущий QJsonObject
    ui->jsonDocumentTextEdit->clear();      // Очищаем текстовое поле
    // Устанавливаем текст всего Json объекта в текстовое поле, чтобы увидеть, что это пустой объект.
    // Увидите следующее -> {}
    ui->jsonDocumentTextEdit->setText(QJsonDocument(m_currentJsonObject).toJson(QJsonDocument::Indented));
}

void Widget::onSaveButtonClicked()
{
    // С помощью диалогового окна получаем имя файла с абсолютным путём
    QString saveFileName = QFileDialog::getSaveFileName(this,
                                                        tr("Save Json File"),
                                                        QString(),
                                                        tr("JSON (*.json)"));
    QFileInfo fileInfo(saveFileName);   // С помощью QFileInfo
    QDir::setCurrent(fileInfo.path());  // установим текущую рабочую директорию, где будет файл, иначе может не заработать
    // Создаём объект файла и открываем его на запись
    QFile jsonFile(saveFileName);
    if (!jsonFile.open(QIODevice::WriteOnly))
    {
        return;
    }

    // Записываем текущий объект Json в файл
    jsonFile.write(QJsonDocument(m_currentJsonObject).toJson(QJsonDocument::Indented));
    jsonFile.close();   // Закрываем файл
}

void Widget::onLoadButtonClicked()
{
    // Выбираем файл
    QString openFileName = QFileDialog::getOpenFileName(this,
                                                        tr("Open Json File"),
                                                        QString(),
                                                        tr("JSON (*.json)"));
    QFileInfo fileInfo(openFileName);   // С помощью QFileInfo
    QDir::setCurrent(fileInfo.path());  // установим текущую рабочую директорию, где будет файл
    // Создаём объект файла и открываем его на чтение
    QFile jsonFile(openFileName);
    if (!jsonFile.open(QIODevice::ReadOnly))
    {
        return;
    }

    // Считываем весь файл
    QByteArray saveData = jsonFile.readAll();
    // Создаём QJsonDocument
    QJsonDocument jsonDocument(QJsonDocument::fromJson(saveData));
    // Из которого выделяем объект в текущий рабочий QJsonObject
    m_currentJsonObject = jsonDocument.object();
    // Очищаем текстовое поле
    ui->jsonDocumentTextEdit->clear();
    // И устанавливаем в проверочное текстовое поле содержимое Json объекта
    ui->jsonDocumentTextEdit->setText(QJsonDocument(m_currentJsonObject).toJson(QJsonDocument::Indented));
}

Заключение

В данном конкретном случае используется QFileDialog, чтобы получить путь к записываемому файлу и открываемому файлу. Конечно из манипуляций с уже существующим файлом доступно только добавление новых текстов и можно открыть любой корректный JSON документ, а после этого добавить в него текст. Но для примера полагаю, что больше и не нужно.

Пример на GitHub репозитории EVILEG

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Михаиллл
  • 28 октября 2018 г. 11:41

Скажите пожалуйста, в 55 строке получаем saveFileNam, это видимо полное имя файла. Но где потом сообщается полный путь  файлу json при его записи?

Evgenii Legotckoi
  • 28 октября 2018 г. 11:50

В saveFileName будет полный путь к файлу с его именем. В диалоге просто вводите имя файла с расширением, а QFileDialog возвращает имя файла и полный путь к нему.

Можете увидеть это, если добавите вывод в qDebug

qDebug() << saveFileName;

А через fileInfo я выдернул путь к файлу, чтобы установить рабочую директорию через QDir::setCurrent();, чтобы указать, где именно сохраняется файл. А когда я передаю saveFileName в QFile, даже с наличием пути в имени, всё будет нормально работать, путь должен быть отброшен автоматически.


Михаиллл
  • 29 октября 2018 г. 2:12

Спасибо.

Скажите пожалуйста, зачем 65 строка? написал по образцу  часть кода, выдало ошибку D:\QTProject\ReaderResume\main.cpp:58: ошибка: return-statement with no value, in function returning 'int' [-fpermissive]

return;

^

Без return работает


Evgenii Legotckoi
  • 29 октября 2018 г. 4:08

зачем вы слот написали с возвращаемым значением int?

У вас сигнатура в вашем коде так написана

int someFunc();

слоту обычно не требуется возвращаемое значение.

return прерывает выполнение метода, чтобы не шло дальше, если файл не открылся в данном случае.


Михаиллл
  • 29 октября 2018 г. 4:26

Спасибо

Скажите пожалуйста, как добавить в массив QJsonArray другой массив QVector<QString> TextArra?

Как я понял, это можно сделать в цикле поочередно для каждого члена массива, но можно ли сразу перевести одной командой?



Evgenii Legotckoi
  • 29 октября 2018 г. 4:40

Думаю, что через алгоритмические методы вставки из стандартной библиотеки и использование итераторов с лямбда функциями можно написать и в одну строку.

Но судя по вашим вопросам, могу сказать, что вы не знакомы со всем этим зоопарком, поэтому пока не забивайте себе этим голову, в процессе работы сами прийдёте к использованию данного функционала. Так что сделайте через цикл поочерёдно. Просто напишите

QJsonArray textsArray;
for (const QString& str : TextArray)
{
        textsArray.push_back(str);
}



Также вы можете сами написать эту вспомогательную команду.

Михаиллл
  • 29 октября 2018 г. 11:07

Спасибо. И скажите пожалуйста, как перевести QString в QJsonValue


Evgenii Legotckoi
  • 29 октября 2018 г. 11:18

QJsonValue::fromVariant(const QVariant& variant);

Михаиллл
  • 29 октября 2018 г. 12:02

Спасибо.

Скажите пожалуйста, в json файле размещен массив, в этот массив нужно добавить еще один элемент массива. Но считывать весь массив - тратить дополнительное время. Можно ли, при условии, что я знаю, что там только один массив, как то быстро добавить один элемент?


Evgenii Legotckoi
  • 29 октября 2018 г. 15:22

Возможно, через итератор можно будет сделать. Я не проверял. сам по себе метод toArray() возвращает копию массива...

На первый взгляд я не увидел необходимой возможности, хотя подумал об этом.

Возможно с итераторами через метод find, можно будет взять массив и уже в нём покопаться. Но не факт.


Михаиллл
  • 31 октября 2018 г. 4:50

А можно сразу перейти в конец документа, если нет ], то записывать с начала, а если есть ] , то ставить запятую и дописывать еще один элемент?

Evgenii Legotckoi
  • 31 октября 2018 г. 4:52

Это уже вручную нужно реализовывать. Только это не нужно, пока действительно не будет проблемы по производительности.

А вообще, если нужно хранение данных, то возможно и насчёт базы данных стоит подумать. Хотя бы SQLite можно использовать. Это будет эффективнее.


Юрий
  • 30 июня 2020 г. 17:34

Как отредактировать данные?

{
    "connect": [
        {
            "id": 1,
            "name": "Data1",
        },
        {
            "id": 2,
            "name": "Data2",
        }
    ]
}

Михаиллл
  • 1 июля 2020 г. 2:54

Смотря что хотите и в каком виде эти данные. Если в текстовом, то их нужно прочитать, перевести в QJsonDocument, из него достать массив connect, и уже обращаясь у элементам массива, получвя из него объекты редактировать их

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
г
  • ги
  • 23 апреля 2024 г. 15:51

C++ - Тест 005. Структуры и Классы

  • Результат:41баллов,
  • Очки рейтинга-8
l
  • laei
  • 23 апреля 2024 г. 9:19

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:10баллов,
  • Очки рейтинга-10
l
  • laei
  • 23 апреля 2024 г. 9:17

C++ - Тест 003. Условия и циклы

  • Результат:50баллов,
  • Очки рейтинга-4
Последние комментарии
k
kmssr8 февраля 2024 г. 18:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 1:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 10:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 8:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 декабря 2023 г. 21:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
G
Gar22 апреля 2024 г. 5:46
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil Academics20 апреля 2024 г. 7:45
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasov14 апреля 2024 г. 6:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 апреля 2024 г. 2:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex4 апреля 2024 г. 4:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Следите за нами в социальных сетях