-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Have a json::type_error exception because of JSON object #1540
Comments
What is the exact type and message of the type_error exception? |
I actually got it figured out. There was an exception here: found = m_cache.insert_or_assign(found, mapkey, std::make_pair(std::chrono::steady_clock::now(), res.body())); and also before where I was using There was also a weird problem where even though the JSON object looked fine when I printed it in the function I'm querying the API in, when I printed the response before sending it to the client-side code, it got explicit newline characters and quotes in it. What I have now is this: // This function queries the currency API for a list of currencies every hour
// Param mapkey: used to search the cache cache storage; if an entry with that key isn't already there
// an initial query will be made to the API with its results cached
const json &cache_storage::query_list(const char *currencykey, const std::string &mapkey, const json &sentry)
{
boost::beast::error_code ec;
auto found = m_cache.find(mapkey);
try
{
if (found == m_cache.end() || (std::chrono::steady_clock::now() - found->second.first) > m_duration)
{
std::string host{ "bankersalgo.com" }, api_endpoint{ "/currencies_names" }, key{ currencykey };
std::string target = api_endpoint + '/' + key;
std::string port{ "443" };
int version = 11;
// The io_context is required for all IO
boost::asio::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ ssl::context::tlsv12_client };
// These objects perform our IO
tcp::resolver resolver{ ioc };
ssl::stream<tcp::socket> stream{ ioc, ctx };
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str()))
{
boost::system::error_code ec{ static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
throw boost::system::system_error{ ec };
}
// Look up the domain name
const auto results = resolver.resolve(host, port);
// This holds the root certificate used for verification
load_root_certificates(ctx);
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_peer);
// Make the connection on the IP address we get from a lookup
boost::asio::connect(stream.next_layer(), results.begin(), results.end());
// Perform the SSL handshake
stream.handshake(ssl::stream_base::client, ec);
if (ec)
{
std::cerr << "Lines 827 and 828:\n";
fail(ec, "handshake");
}
// Set up an HTTP GET request message
http::request<http::string_body> req{ http::verb::get, target, version };
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req.set(http::field::content_type, "application/json");
req.set(http::field::accept, "application/json");
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
boost::beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::string_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
found = m_cache.insert_or_assign(found, mapkey, std::make_pair(std::chrono::steady_clock::now(), res.body()));
// Gracefully close the stream
boost::system::error_code ec;
stream.shutdown(ec);
if (ec == boost::asio::error::eof)
{
// Rationale:
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
ec.assign(0, ec.category());
}
if (ec)
{
throw boost::system::system_error{ ec };
}
// If we get here then the connection is closed gracefully
}
return found->second.second;
}
catch (const std::exception& e)
{
std::cerr << "Line 867: Error: " << e.what() << '\n';
}
return sentry;
} This function queries the currency API for a list of currencies and returns it to There's also a function that queries the API for conversion rates: // This function queries the currency API after making sure
// that the stored result(s) is/are old enough
// It also makes a new query to the API if needed
const json &cache_storage::query_rate(const std::string &query_data, const char *currencykey, const json &sentry)
{
auto found = m_cache.find(query_data);
boost::beast::error_code ec;
try
{
if (found == m_cache.end() || (std::chrono::steady_clock::now() - found->second.first) > m_duration)
{
std::string host{ "bankersalgo.com" }, api_endpoint{ "/apirates2" }, key{ currencykey };
std::string target = api_endpoint + '/' + key + '/' + query_data;
std::string port{ "443" };
int version = 11;
// The io_context is required for all IO
boost::asio::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ ssl::context::tlsv12_client };
// These objects perform our IO
tcp::resolver resolver{ ioc };
ssl::stream<tcp::socket> stream{ ioc, ctx };
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str()))
{
boost::system::error_code ec{ static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
throw boost::system::system_error{ ec };
}
// Look up the domain name
const auto results = resolver.resolve(host, port);
// This holds the root certificate used for verification
load_root_certificates(ctx);
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_peer);
// Make the connection on the IP address we get from a lookup
boost::asio::connect(stream.next_layer(), results.begin(), results.end());
// Perform the SSL handshake
stream.handshake(ssl::stream_base::client, ec);
if (ec)
{
std::cerr << "Lines 725 and 726:\n";
fail(ec, "handshake");
}
// Set up an HTTP GET request message
http::request<http::string_body> req{ http::verb::get, target, version };
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req.set(http::field::content_type, "application/json");
req.set(http::field::accept, "application/json");
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
boost::beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::string_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
found = m_cache.insert_or_assign(found, query_data, std::make_pair(std::chrono::steady_clock::now(), json::parse(res.body())));
// Gracefully close the stream
boost::system::error_code ec;
stream.shutdown(ec);
if (ec == boost::asio::error::eof)
{
// Rationale:
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
ec.assign(0, ec.category());
}
if (ec)
{
throw boost::system::system_error{ ec };
}
// If we get here then the connection is closed gracefully
}
return found->second.second;
}
catch (const std::exception &e)
{
std::cerr << "Line 769: Error: " << e.what() << '\n';
}
return sentry;
} The updated app source code is here. It's a web server app I host on my own computer so I'm not always running it, but when it's running it's available on this address: https://dragonosman.dynu.net:5501/ . You can go to my portfolio site here to see if the server app is running or not (it'll tell you on the My Work page). I want more work to build the portfolio, but right now that's all there is. |
Thanks for checking back! |
@nlohmann
Hi again.
What I want to achieve is pretty much the same as I said in the other issue I opened here, except now I just have a different kind of
json::exception
being thrown that I'm catching. The files I want to read from each have an actual JSON object in them, which looks like this:I'm working on a currency converter application, and the JSON object is a currency list from the currency API I'm using . What I want to do in this part of the app is basically to compare the objects. Right now they're the same, but the idea is to make a new request to the currency API to get the latest currency list and cache it if the contents of one object are ever different from those of the other.
As the title says, I have an exception of type
json::type_error
that says that it expected a string but got an object. Anyway, you can see what I've tried so far in my code that I just updated on GitHub here, where I have that exception being thrown in the functioncache_storage::query_list
on line 805, and also in the functionhandle_request
on line 485 (I have to take out the function callsjson::parse(ifs1)
andjson::parse(ifs2)
in thequery_list
function to be able to see the one inhandle_request
, but they're both there). It's breaking onjson::parse(ifs1)
, but it'd break on the other one if this one weren't there.I'm using Windows 10 Home Single Language, x64. Version and build same as before. Compiler is MSVC in Visual Studio 2017, the latest release version.
I'm using the version of the library from the latest branch.
I want to know what I'm wrong and how to correctly pass a JSON object to
json::parse
such that it doesn't throw atype_error
exception. And I also want to know why what I'm doing inhandle_request
that's causing that exception to be thrown. Any help would be appreciated.Edit: Changed the code a bit, but it's basically still the same. Just that the line the exception is coming from in
cache_storage::query_list
is now 805, since I moved the initialization of the JSON objects.The text was updated successfully, but these errors were encountered: