Boost::JSON, parsing string - c++

So this is my JSON:
string js = R"({"table":"orderBookL2_25","action":"insert","data":[{"symbol":"XBTUSD","id":8796514950,"side":"Buy","size":10000,"price":34850.5},{"symbol":"XBTUSD","id":8796515700,"side":"Buy","size":281,"price":34843}]})";
I want to somehow convert it into object that could help me to conveniently access values. For example, something like that;
parsed_data["data"][0]["symbol"]
I have tried to use JSON::Boost but I don't know how to parse using this library. I have tried this code:
json::value parsed_data{js}; cout << parsed_data.at(0);
But the output from it is worthless:
"{\"table\":\"orderBookL2_25\",\"action\":\"insert\",\"data\":[{\"symbol\":\"XBTUSD\",\"id\":8796514950,\"side\":\"Buy\",\"size\":10000,\"price\":34850.5},{\"symbol\":\"XBTUSD\",\"id\":8796515700,\"side\":\"Buy\",\"size\":281,\"price\":34843}]}"

Basically your code (which you showed in fuzzy way) assigned string to JSon value object, that is why some signs has been escaped.
Problem is that you do not read documentation.
Please read JSon quick look note section how to parse input:
Quick Look - 1.75.0
Parsing
JSON can be parsed into the value container in one step using a free function. In the following snippet, a parse error is indicated by a thrown exception:
value jv = parse( "[1, 2, 3]" );
Error codes are also possible:
error_code ec;
value jv = parse( R"( "Hello, world!" )", ec );
Then fix code for example like this:
#include <iostream>
#include <vector>
#define BOOST_JSON_STACK_BUFFER_SIZE 1024
#include <boost/json/src.hpp>
#include <boost/json.hpp>
using namespace boost::json;
int main()
{
std::string js = R"({
"table":"orderBookL2_25",
"action":"insert",
"data":[
{
"symbol":"XBTUSD",
"id":8796514950,
"side":"Buy",
"size":10000,
"price":34850.5
},
{
"symbol":"XBTUSD",
"id":8796515700,
"side":"Buy",
"size":281,
"price":34843
}
]
})";
try {
auto parsed_data = parse(js);
std::cout << value_to<std::string>(parsed_data.at("data").at(0).at("symbol")) << '\n';
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
return 0;
}
Live demo

Related

access array using 'nlohmann' JSON parser

I am trying to access an JSON array using 'nlohmann' library, as the example below shows:
#include <iostream>
#include <string>
#include "json.hpp"
using json = nlohmann::json;
int main() {
{
const std::string str(
R"(
{
"result":{
"lines":[
{
"i":1,
"w":7,
},
{
"i":1,
"w":8,
}
]
},
"success":true
}
)");
json root(str);
auto result = root.find("result");
if (result != root.end()) {
std::cout << *result << std::endl;
} else {
std::cout << "'result' not found\n";
}
}
}
Can anyone help and explain why the output is 'result' not found? According to the examples I read in https://github.com/nlohmann/json and other references I found, it should work.
I found the error.
I should be json root(json::parse(str)); and not json root(str);

C++ - How to format a file's last modified date and time in the user's preferred date/time locale format in a platform-independent, thread-safe manner

I want to retrieve a given file's last-modified date and time, and format it as a string. I want to do so in a way that is thread- and memory-safe; cross-platform compatible; and does not require the use of custom libraries. The date and time should be formatted according to the user's preferred locale and date/time format, as returned by the operating system.
Using C++11/14 features is fine. So is using Boost. Speed of execution does not particularly matter.
Here is what I have at the moment:
std::string text;
text += filename;
text = "Savegame: "+text+lf+"_________________"+lf;
{
text += "Saved on: ";
const boost::filesystem::path file_name_path{filename};
const boost::filesystem::path save_dir_path{getSaveDir()};
const boost::filesystem::path& full_file_path = boost::filesystem::absolute(file_name_path, save_dir_path);
std::time_t last_saved_time{};
std::stringstream last_saved_string_stream{};
last_saved_string_stream.exceptions(std::ios_base::failbit);
try
{
std::locale users_preferred_locale{std::locale("")};
boost::gregorian::date_facet output_facet{};
last_saved_string_stream.imbue(users_preferred_locale);
last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc(), &output_facet));
output_facet.format("%x");
last_saved_time = boost::filesystem::last_write_time(full_file_path);
auto last_saved_time_point{boost::chrono::system_clock::from_time_t(last_saved_time)};
last_saved_string_stream << last_saved_time_point;
}
catch (boost::filesystem::filesystem_error& fse)
{
VS_LOG_AND_FLUSH(fatal, (boost::format("Filesystem Error determining savegame's last saved date/time: %1%") % fse.what()));
}
catch (std::exception& e)
{
VS_LOG_AND_FLUSH(fatal, (boost::format("General Error determining savegame's last saved date/time: %1%") % e.what()));
}
catch (...)
{
VS_LOG_AND_FLUSH(fatal, "Really bad error determining savegame's last saved date/time! What just happened??");
}
text += last_saved_string_stream.str() + lf;
}
VS_LOG_AND_FLUSH is a macro (I know, I know) that calls BOOST_LOG_TRIVIAL with the specified log level and message, and then flushes all the boost log sinks, plus stdout and stderr.
The implementation of getSaveDir() is not important for purposes of this question. Let's just accept that it returns a std::string containing the directory in which this program's savegames are stored.
Even getting this far was way more difficult than it should have been. I found surprisingly few examples of people actually using these libraries -- especially using them together.
This code compiles, but crashes silently when run on Windows. Meanwhile, on Linux, it runs, but displays the file's timestamp as some huge number of nanoseconds since January 1st, 1970. This is not the correct format. The format should be something like "9/10/2021 5:30 PM" in the US, or "10/09/2021 17:30:00" in some European countries, for example.
What am I doing wrong? And what should I do instead?
I have tried a couple of different format strings on the output_facet.format(... line. I've tried "%c" and "%x", each with the same result.
You're close. Some notes:
facets are owned by their locales and you should not have taken the address of a local variable there:
boost::gregorian::date_facet output_facet{};
last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc(), &output_facet));
output_facet.format("%x");
should be
last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc(),
new boost::gregorian::date_facet("%x")));
In fact, the redundant imbue could probably be merged:
std::stringstream last_saved_string_stream{};
last_saved_string_stream.exceptions(std::ios_base::failbit);
std::locale users_preferred_locale{std::locale("")};
last_saved_string_stream.imbue(users_preferred_locale);
last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc(), new boost::gregorian::date_facet("%x")));
Can become
std::stringstream ss{};
ss.exceptions(std::ios_base::failbit);
ss.imbue(std::locale(std::locale(""), new boost::gregorian::date_facet("%x")));
Note that you can reduce the excess scope on the stringstream and time_t variables
The conversion to chrono time_point is not helping. The facets only apply to Boost DateTime library types, not Chrono. Instead, convert to ptime or local_date_time:
ss << boost::posix_time::from_time_t(
fs::last_write_time(full_file_path));
Why combine string-concatenation, std iostreams and boost::format all in one function?
You can probably go without the imbue, according to my test:
Live On Coliru
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/chrono.hpp>
#include <boost/date_time.hpp>
namespace fs = boost::filesystem;
using namespace std::string_literals;
// mocks
enum severity { fatal };
template <typename Msg>
static void VS_LOG_AND_FLUSH(severity, Msg const& msg) { std::clog << msg << std::endl; }
fs::path getSaveDir() { return fs::current_path(); }
static constexpr char const* lf = "\n";
std::string DescribeSaveGame(std::string filename, bool do_imbue)
{
std::stringstream ss;
ss.exceptions(std::ios_base::failbit);
if (do_imbue) {
ss.imbue(std::locale(std::locale(""),
new boost::gregorian::date_facet("%x")));
}
ss << "Savegame: " << filename << lf << "_________________" << lf;
try {
ss << "Saved on: ";
ss << boost::posix_time::from_time_t(
fs::last_write_time(fs::absolute(filename, getSaveDir())))
<< lf;
return ss.str();
} catch (std::exception const& e) {
VS_LOG_AND_FLUSH(fatal,
"General Error determining savegame's last saved date/time: "s +
e.what());
} catch (...) {
VS_LOG_AND_FLUSH(fatal,
"Really bad error determining savegame's last saved "
"date/time! What just happened??");
}
return "error"; // TODO?
}
int main() {
std::cout << DescribeSaveGame("test.bin", true) << std::endl;
std::cout << DescribeSaveGame("test.bin", false) << std::endl;
}
Printing
Savegame: test.bin
_________________
Saved on: 2021-Sep-11 14:29:09
Savegame: test.bin
_________________
Saved on: 2021-Sep-11 14:29:09
Simplest
The above implies that you can probably simplify the whole thing down to a lexical_cast:
std::string DescribeSaveGame(std::string const& filename) {
auto save_date = boost::posix_time::from_time_t(
last_write_time(absolute(filename, getSaveDir())));
return "Savegame: " + filename + lf + "_________________" + lf +
"Saved on: " + boost::lexical_cast<std::string>(save_date) + lf;
}
Live On Coliru
Still prints
Savegame: test.bin
_________________
Saved on: 2021-Sep-11 14:37:10
First: As it turns out, getSaveDir() was not reliably returning the expected value, after all. That is now fixed.
Second: My catch clauses depended on more formatted string output, after some string output already failed. Not necessarily a good idea.
Third: My catch clauses did not actually exit the program. VS_LOG_AND_FLUSH does not include this functionality.
Fourth: The specific set of header files I included at the top of this .cpp file turned out to matter. Even the order might matter. Here is what I found worked:
#include <boost/filesystem.hpp>
#include <boost/chrono/time_point.hpp>
#include <boost/chrono/io/time_point_io.hpp>
#include <boost/chrono/chrono.hpp>
#include <iostream>
#include <sstream>
#include <chrono>
#include <locale>
Finally: The specific output function I was looking for was boost::chrono::time_fmt.
Here is the code I have now:
string text;
text += filename;
text = "Savegame: "+text+lf+"_________________"+lf;
try
{
text += "Saved on: ";
const boost::filesystem::path file_name_path{filename};
const boost::filesystem::path save_dir_path{getSaveDir()};
const boost::filesystem::path full_file_path{boost::filesystem::absolute(file_name_path, save_dir_path)};
std::time_t last_saved_time{boost::filesystem::last_write_time(full_file_path)};
boost::chrono::system_clock::time_point last_saved_time_point{boost::chrono::system_clock::from_time_t(last_saved_time)};
std::ostringstream last_saved_string_stream{};
last_saved_string_stream << boost::chrono::time_fmt(boost::chrono::timezone::local, "%c")
<< last_saved_time_point;
text += last_saved_string_stream.str() + lf;
}
catch (boost::filesystem::filesystem_error& fse)
{
VS_LOG_AND_FLUSH(fatal, "boost::filesystem::filesystem_error encountered");
VS_LOG_AND_FLUSH(fatal, fse.what());
VSExit(-6);
}
catch (std::exception& e)
{
VS_LOG_AND_FLUSH(fatal, "std::exception encountered");
VS_LOG_AND_FLUSH(fatal, e.what());
VSExit(-6);
}
catch (...)
{
VS_LOG_AND_FLUSH(fatal, "unknown exception type encountered!");
VSExit(-6);
}
This seems to work reliably on both Windows and Linux. (Code not yet tested on macOS, but it does compile on Mac, at least.)
Update 2022-05-13
After following some of the suggestions given in other answers, here is the code I have now. Note that now it uses an ostringstream for pretty much all string concatenation:
std::ostringstream ss{};
ss << "Savegame: " << filename << lf;
ss << "_________________" << lf;
try {
ss << "Saved on: ";
const boost::filesystem::path file_name_path{filename};
const boost::filesystem::path save_dir_path{getSaveDir()};
const boost::filesystem::path full_file_path{boost::filesystem::absolute(file_name_path, save_dir_path)};
std::time_t last_saved_time{boost::filesystem::last_write_time(full_file_path)};
boost::chrono::system_clock::time_point
last_saved_time_point{boost::chrono::system_clock::from_time_t(last_saved_time)};
ss << boost::chrono::time_fmt(boost::chrono::timezone::local, "%c")
<< last_saved_time_point
<< lf;
}
catch (boost::filesystem::filesystem_error &fse) {
VS_LOG_AND_FLUSH(fatal, "boost::filesystem::filesystem_error encountered:");
VS_LOG_AND_FLUSH(fatal, fse.what());
VSExit(-6);
}
catch (std::exception &e) {
VS_LOG_AND_FLUSH(fatal, "std::exception encountered:");
VS_LOG_AND_FLUSH(fatal, e.what());
VSExit(-6);
}
catch (...) {
VS_LOG_AND_FLUSH(fatal, "unknown exception type encountered!");
VSExit(-6);
}
// ...

Add values to map using rapidjson

I get a raw json string
{"vehicle": {"brand": "zonda","color": "blue"},"username": {"brand": "doyota","color": "red"}}
from a get call i make.
I read that rapidjson is the best way to parse a json string in cpp.
So I tried doing something like this:
const char* json = data.c_str();
rapidjson::Document document;
if (document.Parse(json).HasParseError()) {
cout << "has parse error" << endl;
return 1;
}
else {
assert(document.IsObject());
}
Here it says that the json has a parse error. Any idea why this could be?
Also once I am able to parse the values I want to add them as key value pairs to a standard map. Could anyone point me in the right direction to proceed with this?
This gave me no error:
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
using namespace rapidjson;
int main() {
Document d;
std::string json = R"raw({"vehicle": {"brand": "zonda","color": "blue"},"username": {"brand": "doyota","color": "red"}})raw";
if (d.Parse(json.c_str()).HasParseError()) {
std::cout << "has error\n";
} else {
std::cout << "no error\n";
}
}
Tried C++11 - C++20 and it all seems fine. My guess is that you've got some non UTF8 character in the data.

After convert string to const *json, when pasing json object, shows failed: (IsObject()), how to solve this?

After convert string strjson to const char* json, when interate, shows
failed: (IsObject()), function FindMember,failed, I don't understand why showed this, I think this the json object is correct format.
//
// main.cpp
// rapid
//
// Created by Shi Yan on 10/7/17.
// Copyright © 2017 Shi Yan. All rights reserved.
//
#include <iostream>
#include "rapidjson.h"
#include "document.h"
#include <fstream>
using namespace std;
using namespace rapidjson;
void readjson(){
ifstream handle("meta_Books.json");
if(handle.is_open()){
//cout<<"open success"<<endl;
const char* json;
string strjson;
int i=1;
while(getline(handle,strjson)){
if(i>4)
break;
cout<<strjson<<endl;
cout<<strjson.length()<<endl;
i++;
json=strjson.c_str();
cout<<"*********************"<<endl;
cout<<*json<<endl;
StringStream s (json);
Document document;
document.ParseStream(s);
Value::ConstMemberIterator itr = document.FindMember("asin");
cout<<itr->name.GetString()<<" = "<< itr->value.GetString()<<endl;
}
}
}
int main() {
readjson();
return 0;
}
I think the format of json object , so why failed?
As you can see , the getline() method works well, because the output of string is an complete string
The assertion error means that FindMember() is being called on a Value that does not represent a JSON object (IsObject() is false).
Since there is only 1 FindMember() in the code you showed, that implies that document.IsObject() is false when document.FindMember() fails. Either the JSON you are parsing does not start with an object in its root, or the parse failed. Neither condition of which you are testing for in your code.
If I had to guess (and please don't make people guess!), the failing JSON document likely contains an unencoded line break in it (that is not illegal inside of JSON string values). That would cause std::getline() to exit prematurely, thus causing parsing issues.
The 1st screenshot you showed supports that conclusion, showing that strjson is being split between 2 separate "lines" when the error occurs.
Rather than using std::getline() to read the file line-by-line, risking errors on embedded line breaks, I suggest you try using RapidJSON's BasicIStreamWrapper class to read the file document-by-document instead. ParseStream() has a kParseStopWhenDoneFlag flag that allows parsing multiple root documents from a single input stream:
kParseStopWhenDoneFlag 
After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
For example:
#include <iostream>
#include <fstream>
#include "rapidjson.h"
#include "document.h"
#include "istreamwrapper.h"
using namespace std;
using namespace rapidjson;
void readjson()
{
ifstream handle("meta_Books.json");
if (!handle.is_open())
{
// handle error...
cout << "error opening file" << endl;
}
else
{
BasicIStreamWrapper<ifstream> s(handle);
for(int i = 1; i <= 4; ++i)
{
Document document;
ParseResult pr = document.ParseStream<kParseStopWhenDoneFlag>(s);
if (!pr)
{
// handle error...
cout << "error parsing document " << i << endl;
}
else if (!document.IsObject())
{
cout << "document " << i << " is not an object" << endl;
}
else
{
Value::ConstMemberIterator itr = document.FindMember("asin");
if (itr != document.MemberEnd())
cout << "asin = " << itr->value.GetString() << endl;
else
cout << "asin not found" << endl;
}
}
}
}
int main()
{
readjson();
return 0;
}

C++ Rest giving me an error when trying to show JSON file from web

I am trying to make a program that can display a JSON file in console with C++'s REST API. I'm trying to get the JSON file from api.trello.com but every example I come across gives me an error, usually about cbegin() & cend() and how it is not a value of web::json::value...
here is my code:
// The code includes the most frequently used includes necessary to work with C++ REST SDK
#include "cpprest/containerstream.h"
#include "cpprest/filestream.h"
#include "cpprest/http_client.h"
#include "cpprest/json.h"
#include "cpprest/producerconsumerstream.h"
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
using namespace ::pplx;
using namespace utility;
using namespace concurrency::streams;
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace web::json;
using namespace std;
using namespace web;
using namespace web::http;
using namespace web::http::client;
// Retrieves a JSON value from an HTTP request.
pplx::task<void> RequestJSONValueAsync()
{
// TODO: To successfully use this example, you must perform the request
// against a server that provides JSON data.
// This example fails because the returned Content-Type is text/html and not application/json.
http_client client(L"website.com/theRealURLContainsSecretKeys");
return client.request(methods::GET).then([](http_response response) -> pplx::task<json::value>
{
if (response.status_code() == status_codes::OK)
{
return response.extract_json();
}
// Handle error cases, for now return empty json value...
return pplx::task_from_result(json::value());
})
.then([](pplx::task<json::value> previousTask)
{
try
{
const json::value& v = previousTask.get();
// Perform actions here to process the JSON value...
}
catch (const http_exception& e)
{
// Print error.
wostringstream ss;
ss << e.what() << endl;
wcout << ss.str();
}
});
/* Output:
Content-Type must be application/json to extract (is: text/html)
*/
}
// Demonstrates how to iterate over a JSON object.
void IterateJSONValue()
{
// Create a JSON object.
json::value obj;
obj[L"key1"] = json::value::boolean(false);
obj[L"key2"] = json::value::number(44);
obj[L"key3"] = json::value::number(43.6);
obj[L"key4"] = json::value::string(U("str"));
// Loop over each element in the object.
for (auto iter = obj.cbegin(); iter != obj.cend(); ++iter)
{
// Make sure to get the value as const reference otherwise you will end up copying
// the whole JSON value recursively which can be expensive if it is a nested object.
const json::value &str = iter->first;
const json::value &v = iter->second;
// Perform actions here to process each string and value in the JSON object...
std::wcout << L"String: " << str.as_string() << L", Value: " << v.to_string() << endl;
}
/* Output:
String: key1, Value: false
String: key2, Value: 44
String: key3, Value: 43.6
String: key4, Value: str
*/
}
int wmain()
{
// This example uses the task::wait method to ensure that async operations complete before the app exits.
// In most apps, you typically don�t wait for async operations to complete.
wcout << L"Calling RequestJSONValueAsync..." << endl;
RequestJSONValueAsync().wait();
wcout << L"Calling IterateJSONValue..." << endl;
//IterateJSONValue();
system("pause");
}
I am having this error in VS 2015.
The only errors are in IterateJSONValue()
What is my problem and how can I fix it?
json::value does not contain a member function cbegin(). If you access obj.as_object() or obj.as_array() you will find your begin/end members.
// Loop over each element in the object.
for (const auto &pr : obj.as_object()) {
std::wcout << L"String: " << pr.first << L", Value: " << pr.second << endl;
}