So I wanted to write the code for my mini game and need to read a json file for configuration of player. For this I used cereal library.
My code gave me an error, so I reduced it to simpler example but still got an error.
My code:
#include <cereal/types/map.hpp>
#include <cereal/types/string.hpp>
#include <cereal/archives/json.hpp>
#include <string>
#include <map>
#include <iostream>
#include <fstream>
struct Item {
std::string name;
int number;
template <class Archive>
void serialize(Archive &ar) {
ar(cereal::make_nvp("name", name), cereal::make_nvp("number", number));
}
};
int main() {
std::map<std::string, Item> items;
std::ifstream file("dataForTesting.json");
if (!file.is_open()) {
std::cerr << "Could not open the file!" << std::endl;
return 1;
}
try {
cereal::JSONInputArchive archive(file);
archive(items);
} catch (const cereal::Exception& e) {
std::cerr << "Error deserializing JSON: " << e.what() << std::endl;
return 1;
} catch (const std::exception& e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
return 1;
}
for (const auto& item : items) {
std::cout << "Item ID: " << item.first
<< "\nName: " << item.second.name
<< "\nNumber: " << item.second.number << "\n" << std::endl;
}
return 0;
}
My json file:
{
"item1": {
"name": "Item One",
"number": 1
},
"item2": {
"name": "Item Two",
"number": 2
},
"item3": {
"name": "Item Three",
"number": 3
}
}
I compiled via $ c++ testingTesting.c++ -o test -std=c++11 -I.
and executed via $ ./test
and this is an error i got Error deserializing JSON: rapidjson internal assertion failure: IsArray()
Basically you should check documentation of cereal
! It explains how std::map is archived:
cereal Docs - Archive Specialization
Output using cereal built in support:
{ "filter": [ { "key": "type", "value": "sensor" }, { "key": "status", "value": "critical" } ] }
Note it archives data by explicitly stating "key" and "value". Rationale is that key for std::map
do not have to be a std::string
it can be anything and it should be serialized too. Note that in JSon key should be a string.
So by using default built in support for std::map
you have to adjust your JSon:
{
"values": [
{
"key": "item1",
"value": {
"name": "Item One",
"number": 1
}
},
{
"key": "item2",
"value": {
"name": "Item Two",
"number": 2
}
},
{
"key": "item3",
"value": {
"name": "Item Three",
"number": 3
}
}
]
}
Alternatively documentation provides an example how to alter this behavior. See section "Specializing the type".
Here is my attempt to use this appraoch:
#include <cereal/archives/json.hpp>
#include <cereal/types/map.hpp>
#include <cereal/types/string.hpp>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
struct Item {
std::string name;
int number;
template <class Archive>
void serialize(Archive& ar)
{
ar(cereal::make_nvp("name", name), cereal::make_nvp("number", number));
}
};
//! Loading for std::map<std::string, std::string> for text based archives
template <class Archive, class C, class A,
cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae>
inline void cereal::load(Archive& ar, std::map<std::string, Item, C, A>& map)
{
map.clear();
auto hint = map.begin();
while (true) {
const auto namePtr = ar.getNodeName();
if (!namePtr)
break;
std::string key = namePtr;
Item value;
ar(value);
hint = map.emplace_hint(hint, std::move(key), std::move(value));
}
}
int main()
try {
std::map<std::string, Item> items;
cereal::JSONInputArchive archive(std::cin);
archive(cereal::make_nvp("values", items));
for (const auto& item : items) {
std::cout << "Item ID: " << item.first
<< "\nName: " << item.second.name
<< "\nNumber: " << item.second.number << "\n"
<< std::endl;
}
return 0;
} catch (const cereal::Exception& e) {
std::cerr << "Error deserializing JSON: " << e.what() << std::endl;
return 1;
} catch (const std::exception& e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
return 1;
}
There is still extra level in JSon, but tweaking that I'm leaving for you.