Cereal - unable to deserialize a json string - unhandled exception - c++

This is my json string
{
"connectionString" : "MyConnectionString",
"value" :"MyVal"
}
This is my class
struct Settings
{
std::string connectionString;
std::string value;
template<class Archive>
void serialize(Archive& ar)
{
ar(CEREAL_NVP(connectionString),
CEREAL_NVP(value)
);
}
};
And this is what I am doing:
std::ifstream ifs("Settings.json");
std::string content((std::istreambuf_iterator<char>(ifs)),(std::istreambuf_iterator<char>())); // Yes this is valid - The content gets populated
Settings settings;
{
cereal::JSONInputArchive archive_in(ifs);
archive_in(settings); //<<-----Exception here - Unhandled exception
}
The above solution would works only if my json string was this (i.e) if all the json string was an object of another key.
{
"SomeThing" :{
"connectionString" : "MyConnectionString",
"value" :"MyVal"
}
}
My question is how can I make my actual json string work (without wrapping it in an object)? I currently have this in a json file
{
"connectionString" : "MyConnectionString",
"value" :"MyVal"
}
and I wanted to know the best approach of deserializing this into an object ?

The code expecting that outer object is put there by the default JSON "prologue" and "epilogue" behavior:
https://github.com/USCiLab/cereal/blob/10d9a29c225fe9a843d043bfe9f13c5f958844dc/include/cereal/archives/json.hpp#L837
From the documentation:
These functions are given a reference to the active archive and a constant reference to the type being serialized. Careful specialization of prologue and epilogue allows for archives to exercise special behaviors without having to re-implement serialization functions for types. This lets us make the type support fairly generic. For example, cereal::XMLOutputArchive (<cereal/archives/xml.hpp>) makes use of these functions to start and finish nodes in its in-memory tree.
If you add in your code an overload for your type that does nothing:
void epilogue(cereal::JSONInputArchive&, const Settings&) { }
void prologue(cereal::JSONInputArchive&, const Settings&) { }
It won't try to parse an outer object

Related

How to add a variable to spdlog's flag

I want to use spdlog for my code's logging. In my code, there is a important variable for the step in simulation, and I want it to be always displayed in my logs.
Here is the format I wants.
[log_level][the_special_variable][logger_name] messages
So how could format the logger? Or there isn't any way to do that?
Edited:
Sorry I am not good at asking a question in English.
I've read the Readme.md in spdlog's github, and i saw this
// Log patterns can contain custom flags.
// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
{
std::string some_txt = "custom-flag";
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
}
std::unique_ptr<custom_flag_formatter> clone() const override
{
return spdlog::details::make_unique<my_formatter_flag>();
}
};
void custom_flags_example()
{
auto formatter = std::make_unique<spdlog::pattern_formatter>();
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
spdlog::set_formatter(std::move(formatter));
}
but I can't understand the usage of it. It seems like it can only add a string for the custom flag. I would like to kwon if it is OK to display a int variable.
Yes, it is okay to add an int to the log message, you just have to stringify it. For example, in the format method:
auto str = std::to_string(my_special_int_variable);
dest.append(...);
The only question is how you make your int var available in the formatter. The example above assumes it's a global variable.

Array or object: how to use nlohmann::json for simple use cases?

I want to use the nlohmann JSON library in order to read options from a JSON file. Specifying options is optional, as reflected in the constructor in my code example. I'm assuming the JSON structure is an object in its root.
Unfortunately, I'm unable to use these options, because it is unclear to me how I can force the JSON structure to be an object. What is worse, merely initializing a member variable with a JSON object {} (magically?) turns it into an array [{}].
#include <cstdlib>
#include <iostream>
#include <nlohmann/json.hpp>
class Example {
public:
explicit Example(const nlohmann::json& options = nlohmann::json::object())
: m_options{options}
{
std::clog << options << '\n' << m_options << '\n';
}
private:
nlohmann::json m_options;
};
auto main() -> int
{
Example x;
Example y{nlohmann::json::object()};
return EXIT_SUCCESS;
}
This results in the following output. Notice that we have to perform some ceremony in order to use an empty object as the default value (= empty settings), with = nlohmann::json::object(). Also notice that the settings object changes its type as soon as we initialize the member value (!):
{}
[{}]
My use use case is quite straightforward, but I'm unable to extract settings, unless I explicitly check whether the settings are an array or an object.
Another thing that worries me is that incorrect code compiles without warning, e.g., code in which I use x.value("y") on a JSON array x containing an object with key "y". Only at run time do I discover that I should have done x.at(0).value("y") instead.
In brief, the whole situation is quite surprising to me. I must be missing something / I must be using this library in an unintended way?
nlohman is a very "modern" library, it uses a lot of features in C++. And that might make it harder to read and understand the code. But it is very flexible.
This short introduction might help
Introduction to nlohmann json
Parse text to json object is done like
constexpr std::string_view stringJson = R"({"k1": "v1"})";
nlohmann::json j = nlohmann::json::parse( stringJson.begin(), stringJson.end() );

rapidjson producing inconsistent results when converting to string

I currently have a JSON serializer class that I'm using. It's for some experimental code that I'm working on. This code is using cpprestsdk. The serialization is setup to use either rapidjson or cpprestsdk's json.
So for example, the virtual functions for the class look like:
virtual void toJson(rapidjson::Document& json) const =0;
virtual void toJson(web::json::value& json) const =0;
virtual void fromJson(const rapidjson::Value& json) =0;
virtual void fromJson(const web::json::value& json) =0;
I can convert from JSON no problem. I'm currently doing the conversion of a class object to JSON, and then exporting it as a string to a file. I have found that with rapidjson, I get variable results.
On some exports, I see a snippet like this:
"base": {
"name\u0000refere": "base",
On other runs, I see a snippet like this:
"base": {
"name": "base",
This is for successive runs, with no changes to the code.
The fields are actually globally defined const char * like so:
const char *kSymbolKeyName = "name";
const char *kSymbolKeyReferenceName = "referenceName";
The code to generate the JSON object that has the issue looks like:
void Object::toJson(rapidjson::Document& json) const {
using namespace rapidjson;
json.SetObject(); // Reset and clear any existing
auto& allocator = json.GetAllocator();
json.AddMember(StringRef(kObjectKeyName), Value(name.c_str(), allocator), allocator);
json.AddMember(StringRef(kObjectKeyPrioritizeTable), Value(prioritizeTable), allocator);
json.AddMember(StringRef(kObjectKeyPrioritizeGreaterOn), Value(prioritizeGreaterOn), allocator);
}
Note that kObjectKeyNameis defined as const char *kObjectKeyName = "name";.
And the caller to this class's toJson would look like:
using namespace rapidjson;
json.SetObject(); // Reset and clear any existing
auto& allocator = json.GetAllocator();
for (const auto& it : tables) {
Document iJson;
it.second->toJson(iJson);
json.AddMember(Value(it.first.c_str(), allocator), iJson, allocator);
}
Part of the problem may stem from the way I am using rapidjson::Documents and allocators. I believe toJsoncall will end up with it's own allocator once I make the SetObjectcall.
My plan is to revamp the code to use Value instead of Document in toJson and then pass the allocator in as an argument. I ideally didn't want to do that mainly because I was being lazy and wanted the signature to the same so it was easy to flip between rapidjson or cppsrestsdk's son.
Oh yeah, the code to output the file as a string is the following
std::ofstream out("output.json");
rapidjson::Document outDoc;
dataSet.toJson(outDoc);
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
outDoc.Accept(writer);
out << buffer.GetString();
out.close();
There is no doubt something that I am doing odd/dumb as just recently started using rapidjson. I'm just trying to narrow down my issues and better understand the error of my ways.
It appears if modifying the process to pass in an allocator works.
I modified my toJson function to be
rapidjson::Value toJson(rapidjson::Document::AllocatorType& allocator);
In my usage, it means all generated Values, when needed, will use the base Document's allocator.

Error handling when parsing text file to object

I want to parse a simple text file and create an object from the data it contains. I'm using C++11 for this (and I'm not fluent).
In case of any kind of error (e.g. missing file or invalid text) I wish to tell the caller of my parsing function what went wrong, providing information like what kind of error occurred and where in the file.
I don't consider exceptional that errors occur while parsing, so it seems exceptions are not the way to go.
I thought of returning a struct with all the info, including the resulting parsed object in case of success:
struct ParsingResult
{
bool success;
int errorCode;
int errorLine;
ParsedObject object;
}
However I'm not convinced by this solution because, in case of errors, I must still provide a ParsedObject. I can define a default constructor for that, of course, but by it's nature a ParsedObject makes sense only when the parsing is successful.
I could change ParsedObject to ParsedObject*, but I'm reluctant to use pointers when not necessary, and I wonder if this can be avoided.
My question: can you suggest a better solution to this problem? What is it?
struct Obj
{
// your object from the data...
}
struct ParseError
{
int errorCode;
int errorLine;
}
class Parser
{
ParseError m_error;
// other things
public:
bool parse(const std::string& filename, Obj& parsedObject)
{
// Open file, etc...
//parsedObject.property1 = some_value;
// if something is wrong, set m_error and return false;
// return true if ok
}
ParseError getLastError() { return m_error; }
}
// in your code
Parser p;
Obj o;
if(!p.parse("filename", o))
{
// ParseError = p.getLastError();
}
You can use a
unique_ptr<ParsedObject>
or
shared_ptr<ParsedObject>
whose default constructor can be compared to null in case of unsuccessful parsing.
Avoiding raw pointers will free you from having to free memory :)

Rapidjson assertions during HasMember

To all those that are familiar with rapidjson i have the following issue:
I have a certain function that accepts as parameter a date and if that date exists in the json file the method does some operations and if not some other operations.
Generally it looks like this: (not actual code more like pseudo)
Function:
void updateData(string date) {
//
//code to turn date from string to const char* (tested)
//
if (v.HasMember(date)) { //v is a value
Value d;
d=v[date];
//
//code that involves getting data from d (d is object) using HasMember
//
} else {
//generic code that has nothing to do with json
}
JSON file:
{
"main": {
"v": {
"2014-10-02" : {
//some fields
},
"2014-10-03" : {
//some fields
}
}
}
}
So the first time that i call updateData for date "2014-10-02" it runs correctly(executes the if part).
The problem is when i call updateData for another date (like "2014-10-03" that is supposed to work well) it always executes the wrong part(else part) and even when i switch back to the first date it still executes the else part. (while popping many assertions (mostly isString())).
So is HasMember really the problem here, in the sense that it is maybe altering the object?
And are there any alternative ways to search for a member, other than that?
Any tip is appreciated...
Its hard to tell without the actual code, but I think problem might be that you are treating "v" as a Value instead of an Object. "v" isn't a value, its the name of the object. So what you have is a nested object. In order to do this I think you would have to used MemberIterators and iterate through the child objects in the v object.
rapidjson has a pretty good example on how to use iterators.
there is also this question here, which has a pretty good answer on how to use nested objects
Retrieving a nested object inside a JSON string using rapidjson