Как сохранить результаты curl-запроса в Json::Value?

Проблема в следующем, надо прочитать json файл с сайта и занести его в json::value. Пытался сделать так

#include <iostream>
#include "curl/curl.h"
#include "json/json.h"

using namespace std;

int main(int argc, char* argv[])
{
 Json::Value message;
 CURL* req = curl_easy_init();
 CURLcode res;
 curl_easy_setopt(req, CURLOPT_URL, "ссылка");
 curl_easy_setopt(req, CURLOPT_FOLLOWLOCATION, 1L);
 message =  curl_easy_perform(req);
 cout <<  message;
}

Но почему-то в message ноль. Каким образом в него можно занести данные, полученные с сайта?


Ответы (1 шт):

Автор решения: isnullxbh

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

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onDataReceived);

Функция onDataReceived принимает следующие параметры:

  • ptr - указатель на буфер с полученными данными
  • size - всегда равен 1 (или в большинстве случаев)
  • nmemb - размер буфера
  • userdata - см. ниже

Кроме того, необходимо указать буфер, в который будут записаны полученные данные:

curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf);

, где buf - указатель на буфер, который можно получить в функции onDataReceived приведением userdata к нужному типу.

Небольшой пример:

#include <iostream>
#include <sstream>
#include <string>

#include <curl/curl.h>
#include <json/json.h>

auto onDataReceived(char* ptr, std::size_t size, std::size_t nmemb, void* userdata) -> std::size_t;

constexpr auto k_max_buffer_size = 4096;

auto main() -> int
{
    std::string data;

    bool request_complete = true;

    if (auto curl = curl_easy_init())
    {
        curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/users/hadley/orgs");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onDataReceived);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "Ninja");

        if (curl_easy_perform(curl) != CURLE_OK)
        {
            request_complete = false;
            std::cerr << "An error occurred while executing the request" << std::endl;
        }

        curl_easy_cleanup(curl);
    }

    if (request_complete)
    {
        std::istringstream stream { data };
        Json::Value json;
        stream >> json;

        std::cout << json.toStyledString() << std::endl;
    }
}

auto onDataReceived(char* ptr, std::size_t size, std::size_t nmemb, void* data) -> std::size_t
{
    if (auto str = reinterpret_cast<std::string*>(data))
    {
        str->append(ptr, nmemb * size);
    }
    return nmemb * size;
}

Здесь в качестве буфера я использую объект типа std::string, так как мне лень использовать массивы и следить за размером буфера (т.к. мы не знаем заранее, сколько байт нужно для того, чтобы сохранить полученные данные).

Что касается добавления хедера User-Agent - добавил только потому, что при отсутствии последнего данный сервис возвращает ошибку. Если используемый Вами ресурс не требует данного хедера - уберите.

Вывод должен быть примерно следующим:

[
    {
        "avatar_url" : "https://avatars1.githubusercontent.com/u/423638?v=4",
        "description" : "",
        "events_url" : "https://api.github.com/orgs/ggobi/events",
        "hooks_url" : "https://api.github.com/orgs/ggobi/hooks",
        "id" : 423638,
        "issues_url" : "https://api.github.com/orgs/ggobi/issues",
        "login" : "ggobi",
        "members_url" : "https://api.github.com/orgs/ggobi/members{/member}",
        "node_id" : "MDEyOk9yZ2FuaXphdGlvbjQyMzYzOA==",
        "public_members_url" : "https://api.github.com/orgs/ggobi/public_members{/member}",
        "repos_url" : "https://api.github.com/orgs/ggobi/repos",
        "url" : "https://api.github.com/orgs/ggobi"
    },
    ...
]
→ Ссылка