I am currently trying to generate JSON with ordered keys and therefore used a workaround method. However, if I try to use it within my base and derived classes, I get an error which I do not really understand. It seems like it fails to call the to_Json methods (because the error appears if I try to map a DerivedClass-instance (test and test2) to my_json.
I have already tried the example without ordered keys (just by using json = nlohmann::json;) and it works completely fine. The keys in the output are sorted alphabetically and looks like this:
{
"cbor": "cbortest",
"diagnostic": "diagnose: corona",
"header": {
"headerId": 3,
"timestamp": "2019-12-10T16:04:00.00Z",
"version": "4.0.0"
},
"hex": "00f2",
"roundtrip": true
}
What I am trying to achieve through using the nlohmann fifo_map is to keep the insertion order and the final output therefore should look like this:
{
"header": {
"headerId": 3,
"timestamp": "2019-12-10T16:04:00.00Z",
"version": "4.0.0"
},
"cbor": "cbortest",
"hex": "00f2",
"roundtrip": true,
"diagnostic": "diagnose: corona"
}
Executing the following code outputs two errors:
Error C2440: 'initializing': cannot convert from 'BaseNamespace::SubNamespace::DerivedClass' to 'nlohmann::basic_json<my_workaround_fifo_map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>' ; in file: main.cpp
Please have a look at the following code:
In BaseClass.h:
#ifndef BASECLASS_H
#define BASECLASS_H
#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include "fifo_map.hpp"
namespace BaseNamespace{
namespace SubNamespace{
class BaseClass {
public:
BaseClass () {};
virtual ~BaseClass () {};
uint32_t getHeaderId() const { return headerId; };
std::string getTimestamp() const { return timestamp; };
std::string getVersion() const { return version; };
void setHeaderId(uint32_t str) { headerId = str; };
void setTimestamp(std::string str) { timestamp = str; };
void setVersion(std::string bo) { version = bo; };
void setHeader(UAgvHeader const& header) {
setHeaderId(header.getHeaderId());
setTimestamp(header.getTimestamp());
setVersion(header.getVersion());
}
private:
uint32_t headerId;
std::string timestamp;
std::string version;
};
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
using namespace nlohmann;
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
void to_json(my_json &j, const BaseClass &p)
{
j = my_json{
{ "headerId", p.getHeaderId() },
{ "timestamp", p.getTimestamp() },
{ "version", p.getVersion() }
};
}
void from_json(const my_json &j, BaseClass &p)
{
p.setHeaderId(j.at("headerId").get< std::uint32_t>());
p.setTimestamp(j.at("timestamp").get< std::string >());
p.setVersion(j.at("version").get<std::string>());
}
} // namespace SubNamespace
} // namespace BaseNamespace
#endif // BASECLASS_H_
In DerivedClass.h:
#ifndef DERIVEDCLASS_H
#define DERIVEDCLASS_H
#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include <optional>
#include "BaseClass.h"
namespace BaseNamespace{
namespace SubNamespace{
class DerivedClass : public BaseClass {
public:
std::string getCBor() const { return cbor; };
std::string getHex() const { return hex; };
bool getRoundtrip() const { return roundtrip; };
std::optional<std::string> getDiagnostic() const { return diagnostic; };
void setCBor(std::string str) { cbor = str; };
void setHex(std::string str) { hex = str; };
void setRoundtrip(bool bo) { roundtrip = bo; };
void setDiagnostic(std::optional<std::string> opt_str) { diagnostic = opt_str; };
private:
std::string cbor;
std::string hex;
bool roundtrip;
std::optional<std::string> diagnostic = std::nullopt;
};
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
using namespace nlohmann;
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
void to_json(my_json &j, const DerivedClass& p)
{
j["header"] = static_cast<BaseClass>(p);
j["cbor"] = p.getCBor();
j["hex"] = p.getHex();
j["roundtrip"] = p.getRoundtrip();
// assuming you only want a "diagnostic" key if there is an actual value;
// if not, store a nullptr and adjust the from_json accordingly
if (p.getDiagnostic() != std::nullopt)
{
j["diagnostic"] = p.getDiagnostic().value();
}
}
void from_json(const my_json &j, DerivedClass&p)
{
p.setHeader(j.at("header").get<BaseClass>());
p.setCBor(j.at("cbor").get< std::string >());
p.setHex(j.at("hex").get< std::string >());
p.setRoundtrip(j.at("roundtrip").get< bool >());
// if we also allow "null" values, then we need to add an "is_string()"
// check
if (j.count("diagnostic") != 0)
{
p.setDiagnostic(j.at("diagnostic").get< std::string >());
}
}
} // namespace SubNamespace
} // namespace BaseNamespace
#endif // DERIVEDCLASS_H
In main.cpp:
#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
#include <iomanip>
#include <optional>
#include "DerivedClass.h"
using namespace nlohmann;
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
int main(int argc, char* argv[]) {
BaseNamespace::SubNamespace::DerivedClass test;
test.setHeaderId(3);
test.setTimestamp("2019-12-10T16:04:00.00Z");
test.setVersion("4.0.0");
test.setCBor("cbortest");
test.setHex("00f2");
test.setRoundtrip(true);
test.setDiagnostic("diagnose: corona");
my_json j = test; // ERROR: no suitable conversion
std::cout << std::setw(2) << j << std::endl;
std::string str = R"({"header":
{ "headerId" : 4711,
"timestamp" : "1 Uhr",
"version" : "5.0.0"
},
"cbor" : "+X4A",
"hex" : "f97e00",
"roundtrip" : true,
"diagnostic" : "NaN"
})";
my_json j2 = my_json::parse(str);
BaseNamespace::SubNamespace::DerivedClass test2 = j2;
my_json k = test2; // ERROR: no suitable conversion
std::cout << std::setw(2) << k << std::endl;
return 0;
}