Четверг, 14 января 2021 19:00

Работа с WebAPI в Qt5 на примере EVE Online ESI. Часть 1.

Россия
Оцените материал
(0 голосов)

Qt5 обладает богатыми возможностями для работы с WebAPI. Сегодня мы рассмотрим базовый функционал работы с WebAPI на примере запроса цен на минерал Tritanium в регионе The Forge из игры EVE Online с помощью ESI – EVE Swagger Interface.

URL запроса данных

Для запроса мы будем использовать URL - https://esi.evetech.net/dev/markets/10000002/orders/?datasource=tranquility&order_type=sell&type_id=34

Вы можете открыть его в браузере и получите ответ в формате JSON.

Разбираемся с QNetworkAccessManager

В .pro файле нашего проекта изменим первую строку на

QT       += core gui network

Таким образом мы подключаем к нашему проекту модуль и библиотеки для работы с сетью.

Для работы с сетью в Qt5 используется класс QNetworkAccessManager. Он содержит все методы, необходимые для запроса/отправки данных.

Добавим в конструктор формы код:

    QNetworkAccessManager *netMan = new QNetworkAccessManager(this);

    QUrl url("https://esi.evetech.net/dev/markets/10000002/orders/?datasource=tranquility&order_type=sell&type_id=34");

 Здесь мы создаём экземпляр класса QNetworkAccessManager и локальную переменную содержащую наш URL

Работа с WebAPI это всегда операции асинхронного ввода\вывода, поэтому нам не обойтись без сигналов и слотов, так как нельзя заранее сказать, сколько времени займет запрос данных из сети.

Добавим слот срабатывающий в результате получения какого-либо результата при запросе, это может быть как успешный запрос, так и ошибка:

void MainWindow::onResult(QNetworkReply *reply)
{
    if(reply->error() == QNetworkReply::NoError) {

            QStringList propertyNames;
            QStringList propertyKeys;

            QString strReply = (QString)reply->readAll();

            qDebug() << strReply;
    }
}

Соединим этот слот с сигналом от QNetworkAccessManager

    QObject::connect(netMan, SIGNAL(finished(QNetworkReply*)), this, SLOT(onResult(QNetworkReply*)));

Осуществим запрос к внешнему ресурсу:

    QNetworkReply* reply = netMan->get(QNetworkRequest(url));

 Запустим – в консоли появится вывод

{\"duration\":90,\"is_buy_order\":false,\"issued\":\"2021-01-02T17:46:51Z\",\"location_id\":60005599,\"min_volume\":1,\"order_id\":5887873917,\"price\":9.0,\"range\":\"region\",\"system_id\":30000119,\"type_id\":34,\"volume_remain\":703820,\"volume_total\":810000},{\"duration\":90,\"is_buy_order\":false,\"issued\":\"2021-01-09T18:15:34Z\",\"location_id\":60004462,\"min_volume\":1,\"order_id\":5893950313,\"price\":6.8,\"range\":\"region\",\"system_id\":30000119,\"type_id\":34,\"volume_remain\":6397897,\"volume_total\":12423679}

Я сократил вывод, так как в ответе большое количество строк.

Таким образом в результате запроса мы получили строку в формате JSON.

Давайте преобразуем её в удобную форму:

QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8()); 

QJsonArray jsonArray = jsonResponse.array(); 

qDebug() << jsonArray;

 Запустим – на этот раз мы получили массив.

QJsonArray([{"duration":90,"is_buy_order":false,"issued":"2021-01-02T17:46:51Z","location_id":60005599,"min_volume":1,"order_id":5887873917,"price":9,"range":"region","system_id":30000119,"type_id":34,"volume_remain":703820,"volume_total":810000}

Чтобы получить доступ к значениям мы можем преобразовать значение в объект: 

QJsonObject jsonObject = jsonArray[0].toObject(); 

qDebug() << jsonObject;

 

QJsonObject({"duration":90,"is_buy_order":false,"issued":"2021-01-02T17:46:51Z","location_id":60005599,"min_volume":1,"order_id":5887873917,"price":9,"range":"region","system_id":30000119,"type_id":34,"volume_remain":703820,"volume_total":810000})

Теперь мы можем получить доступ к полям: 

qDebug() << jsonObject.value("price");
QJsonValue(double, 9)

Или

qDebug() << jsonObject["price"].toDouble();
9

Обратите внимание, в последнем варианте нужно использовать метод подходящий для значения, например код:

qDebug() << jsonObject["price"].toString();

вернет

""

Так как у нас в JSON указан тип данных - число:

,"price":9

 

qDebug() << jsonObject["price"].toInt();

вернет

9

Создаем класс для наших данных

Мы смогли получить данные из сети. Но хранить их в виде массива QJsonObject неудобно.

Давайте создадим класс для хранения данных – QEveESIMarketOrder

Заголовок:

#ifndef QEVEESIMARKETORDER_H
#define QEVEESIMARKETORDER_H

#include <QString>



class QEveESIMarketOrder
{
public:
    QEveESIMarketOrder();

    int getDuration() const;
    void setDuration(int value);

    QString getIs_buy_order() const;
    void setIs_buy_order(const QString &value);

    QString getIssued() const;
    void setIssued(const QString &value);

    int getLocation_id() const;
    void setLocation_id(int value);

    int getMin_volume() const;
    void setMin_volume(int value);

    int getOrder_id() const;
    void setOrder_id(int value);

    float getPrice() const;
    void setPrice(float value);

    QString getRange() const;
    void setRange(const QString &value);

    int getSystem_id() const;
    void setSystem_id(int value);

    int getType_id() const;
    void setType_id(int value);

    int getVolume_remain() const;
    void setVolume_remain(int value);

    int getVolume_total() const;
    void setVolume_total(int value);

private:

    int duration;
    QString is_buy_order;
    QString issued;
    int location_id;
    int min_volume;
    int order_id;
    float price;
    QString range;
    int system_id;
    int type_id;
    int volume_remain;
    int volume_total;
};

#endif // QEVEESIMARKETORDER_H

Реализация: 

void QEveESIMarketOrder::setIs_buy_order(const QString &value)
{
    is_buy_order = value;
}

QString QEveESIMarketOrder::getIssued() const
{
    return issued;
}

void QEveESIMarketOrder::setIssued(const QString &value)
{
    issued = value;
}

int QEveESIMarketOrder::getLocation_id() const
{
    return location_id;
}

void QEveESIMarketOrder::setLocation_id(int value)
{
    location_id = value;
}

int QEveESIMarketOrder::getMin_volume() const
{
    return min_volume;
}

void QEveESIMarketOrder::setMin_volume(int value)
{
    min_volume = value;
}

int QEveESIMarketOrder::getOrder_id() const
{
    return order_id;
}

void QEveESIMarketOrder::setOrder_id(int value)
{
    order_id = value;
}

float QEveESIMarketOrder::getPrice() const
{
    return price;
}

void QEveESIMarketOrder::setPrice(float value)
{
    price = value;
}

QString QEveESIMarketOrder::getRange() const
{
    return range;
}

void QEveESIMarketOrder::setRange(const QString &value)
{
    range = value;
}

int QEveESIMarketOrder::getSystem_id() const
{
    return system_id;
}

void QEveESIMarketOrder::setSystem_id(int value)
{
    system_id = value;
}

int QEveESIMarketOrder::getType_id() const
{
    return type_id;
}

void QEveESIMarketOrder::setType_id(int value)
{
    type_id = value;
}

int QEveESIMarketOrder::getVolume_remain() const
{
    return volume_remain;
}

void QEveESIMarketOrder::setVolume_remain(int value)
{
    volume_remain = value;
}

int QEveESIMarketOrder::getVolume_total() const
{
    return volume_total;
}

void QEveESIMarketOrder::setVolume_total(int value)
{
    volume_total = value;
}

Я не стал переименовывать названия полей, чтобы сэкономить время.

Добавим конструктор, для заполнения полей класса данными:

QEveESIMarketOrder::QEveESIMarketOrder(QJsonObject data)
{
    this->duration = data["duration"].toInt();
    this->is_buy_order = data["is_buy_order"].toString();
    this->issued = data["issued"].toString();
    this->location_id = data["location_id"].toInt();
    this->min_volume = data["min_volume"].toInt();
    this->order_id = data["order_id"].toInt();
    this->price = data["price"].toDouble();
    this->range = data["range"].toString();
    this->system_id = data["system_id"].toInt();
    this->type_id = data["type_id"].toInt();
    this->volume_remain = data["volume_remain"].toInt();
    this->volume_total = data["volume_total"].toInt();
}

Перегружаем оператор << 

Перегрузка << подробно рассмотрена в  статье.

Заголовок:

QDebug operator<<(QDebug debug, const QEveESIMarketOrder &order);
QDebug operator<<(QDebug debug, const QEveESIMarketOrder *order);

Реализация:

QDebug operator<<(QDebug debug, const QEveESIMarketOrder &order)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << "QEveESIMarketOrder ("
                    << " duration: " << order.getDuration() << ","
                    << " is_buy_order: " << order.getIs_buy_order() << ","
                    << " issued: " << order.getIssued() << ","
                    << " location_id: " << order.getLocation_id() << ","
                    << " min_volume: " << order.getMin_volume() << ","
                    << " order_id: " << order.getOrder_id() << ","
                    << " price: " << order.getPrice() << ","
                    << " range: " << order.getRange() << ","
                    << " system_id: " << order.getSystem_id() << ","
                    << " type_id: " << order.getType_id() << ","
                    << " volume_remain: " << order.getVolume_remain() << ","
                    << " volume_total: " << order.getVolume_total()
                    << ")";
    return debug;
}

QDebug operator<<(QDebug debug, const QEveESIMarketOrder *order)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << *order;
    return debug;
}

Добавим код:

            QEveESIMarketOrder *order = new QEveESIMarketOrder(jsonObject);
            qDebug() << order;

Запустим:

QEveESIMarketOrder ( duration: 90, is_buy_order: "", issued: "2021-01-02T17:46:51Z", location_id: 60005599, min_volume: 1, order_id: 0, price: 9, range: "region", system_id: 30000119, type_id: 34, volume_remain: 703820, volume_total: 810000)

Загрузка списка ордеров

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

            QList<QEveESIMarketOrder*> *ordersist = new  QList<QEveESIMarketOrder*>();

            foreach (QJsonValue jsonObject, jsonArray) {
                QEveESIMarketOrder *order = new QEveESIMarketOrder(jsonObject.toObject());
                ordersist->append(order);
            }

            qDebug() << ordersist->toVector();

Запустим:

QVector(QEveESIMarketOrder ( duration: 90, is_buy_order: "", issued: "2021-01-02T17:46:51Z", location_id: 60005599, min_volume: 1, order_id: 0, price: 9, range: "region", system_id: 30000119, type_id: 34, volume_remain: 703820, volume_total: 810000), QEveESIMarketOrder ( duration: 90, is_buy_order: "", issued: "2021-01-09T18:15:34Z", location_id: 60004462, min_volume: 1, order_id: 0, price: 6.8, range: "region", system_id: 30000119, type_id: 34, volume_remain: 6397887, volume_total: 12423679), QEveESIMarketOrder ( duration: 90, is_buy_order: "", issued: "2020-12-02T09:52:13Z", location_id: 60005455, min_volume: 1, order_id: 0, price: 8, range: "region", system_id: 30000128, type_id: 34, volume_remain: 76320, volume_total: 131652), QEveESIMarketOrder ( duration: 90, is_buy_order: "", issued: "2020-12-18T20:38:32Z", location_id: 60005455, min_volume: 1, order_id: 0, price: 10.5, range: "region", system_id: 30000128, type_id: 34, volume_remain: 291816, volume_total: 291816), QEveESIMarketOrder ( duration: 90, is_buy_order: "", issued: "2021-01-07T07:00:37Z", location_id: 60003229, min_volume: 1, order_id: 0, price: 10.37, range: "region", system_id: 30000128, type_id: 34, volume_remain: 53888, volume_total: 53888), …

Вывод сокращен.

Заключение

Сегодня мы разобрали как работает класс QNetworkAccessManager.

Сделали запрос к WebAPI (ESI) игры EVE Online.

Получили результат в JSON и рассмотрели способ парсинга информации в таком формате.

Создали класс для хранения данных об ордерах.

Загрузили список ордеров в коллекцию QList.

Найти исходный код проекта вы можете на Github.

Прочитано 207 раз Последнее изменение Четверг, 14 января 2021 19:37