Работа с WebAPI в Qt на примере EVE Online ESI. Часть 1
Qt обладает богатыми возможностями для работы с 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.
Найти исходный код проекта вы можете на GitFlic.
Добавить комментарий