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, и уже обращаясь у элементам массива, получвя из него объекты редактировать их

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
AD

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

  • Результат:50баллов,
  • Очки рейтинга-4
m
  • molni99
  • 26 октября 2024 г. 11:37

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

  • Результат:80баллов,
  • Очки рейтинга4
m
  • molni99
  • 26 октября 2024 г. 11:29

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

  • Результат:20баллов,
  • Очки рейтинга-10
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 22:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi1 ноября 2024 г. 0:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 18:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 17:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 21:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
Evgenii Legotckoi
Evgenii Legotckoi25 июня 2024 г. 1:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 ноября 2024 г. 17:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject4 июня 2022 г. 13:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 октября 2024 г. 19:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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