Hi
I need to deserialize a array that have derived objects.
{
"systems":
[
{"type": "system1", "name": "system_1" special_param_1 : 1 },
{"type": "system2", "name": "system_2", special_param_2 : "Value2" },
{"type": "system3", "name": "system_3", special_param_3 : 3.0, special_param_3_1 : "Value_3" }
]
}
so in my from_json I have to do what? Sorry but I do not realy know how to solve this.
I started with:
void from_json(cons json& j, settings& settings)
{
settings.systems = j.at("systems").get<vector<systembase>>();
??
}
void from_json(cons json& j, systembase& base)
{
base.type =j.at["type"].get<string>();
base.name =j.at["name"].get<string>();
switch (base.type)
{
case :
...?
}
}
void from_json(cons json& j, systemderived_1& derived)
{
derived = j.at["special_param_1"].get<int>();
}
I am not sure what you want to do exactly, but this may be helpful:
#include <iostream>
#include <fstream>
#include <array>
#include "json.hpp"
using json = nlohmann::json;
class systembase
{
public:
std::string type;
std::string name;
};
void from_json(const json& j, systembase& sb) {
sb.type = j.at("type");
sb.name = j.at("name");
}
class settings {
public:
std::vector<systembase> systems;
};
void from_json(const json& j, settings& s) {
const json& sj = j.at("systems");
s.systems.resize(sj.size());
std::copy(sj.begin(), sj.end(), s.systems.begin());
}
int main() {
std::string text = R"(
{
"systems": [
{
"type": "system1",
"name": "system_1",
"special_param_1": 1
},
{
"type": "system2",
"name": "system_2",
"special_param_2": "Value2"
},
{
"type": "system3",
"name": "system_3",
"special_param_3": 3.0,
"special_param_3_1": "Value_3"
}
]
}
)";
json j = json::parse(text);
settings s = j;
}
Than I have only deserilized the systembaseclass with parameter type and name. But I have not deserialized the derived classes with the special parameter
I solved it now in this way
void from_json(const json& j, shared_ptr<systembase>& sb)
{
// used Better_Enum
const auto type = SystemType::_from_string_nocase(j.at("type").get<string>().c_str());
switch (type)
{
case SystemType::Derived1:
sb= j.get<shared_ptr<systemderived_1>>();
break;
case SystemType::Derived2:
sb= j.get<shared_ptr<systemderived_2>>();
break;
default: ;
}
sb->type = type;
sb->name = j.at("name").get<string>();
}
void from_json(const json& j, shared_ptr<systemderived_1>& sd)
{
sd= make_shared<systemderived_1>();
sd->special_param_1= j.at("special_param_1").get<int>();
}
Thanks for checking back!
Hi... you closed... ok so you also the opinion thats the best/only solution?
I have no real opinion on this. If it works for you, then it's OK. If not, are there issues left?
Looks good to me.
I think its not possible to determine the type of derived class automaticaly. So the way with the switch was the only solution for me.
The to_json method for the sake of completeness
void to_json(json& j, const shared_ptr<systembase>& sb)
{
switch (sb->type)
{
case SystemType::Derived1:
j = json(dynamic_pointer_cast<systemderived_1>(sb));
break;
case SystemType::Derived2:
j = json(dynamic_pointer_cast<systemderived_2>(sb));
break;
default: ;
}
j["type"] = metaData->type._to_string();
j["name"] = metaData->name;
}
void to_json(json& j, const shared_ptr<systemderived_1>& sd)
{
j = json
{
{ "special_param_1", sd->special_param_1}
};
}
void to_json(json& j, const shared_ptr<systemderived_2>& sd)
{
j = json
{
{ "special_param_2", sd->special_param_2}
};
}
Sorry if I am reopening a can of worms…
I have a similar issue, a base class, and two derived classes. The base class handles the bulk of the serialization, and the derived classes have some unique properties which also need to be serialized. The difference in perspective is that there will be more derived classes down the road, which might include “user defined” classes. Generally it would be messy for the base class to have to know about all the potential derived classes (e.g. in a big switch statement as in MattiasEppler’s to_json example above).
I already have a solution to the basic functionality I need. I define a virtual function on the base class (virtual void serializeToJson(nlohmann::json& j);) and override that in the derived classes. (The overrides calls the base class method then do their specialize stuff.) This is fine, but does not provide the nice syntax that I could get from using the standard to_json() interface.
So I am not stuck, but it would be more convenient if I could use to_json() and treat it as a virtual function I could override. In the meantime I will continue to use my _ad hoc_ interface.
@theodelrieu to the rescue...
Maybe my desired use case is supported by the existing implementation. Say I have a base class with an inheritable serialization method, lets call it to_json_helper() as well as the standard to_json():
class Fruit
{
virtual void to_json_helper(json& j)
{
j = {{"edible", true}, {“contains_seeds”, true}};
}
static void to_json(json& j, const Fruit& f) { f.to_json_helper(j); };
};
Then a derived class would look like:
class Kumquat : public Fruit
{
void to_json_helper(json& j) override
{
Fruit::to_json_helper(j);
j["vitamin_c"] = true;
}
static void to_json(json& j, const Kumquat& k) { k.to_json_helper(j); };
};
However, I guess if I have a generic Fruit reference to an instance of Kumquat, then try to invoke the to_json interface, I will not get the derived version:
j.push_back(fruit); // uses Fruit::to_json() not Kumquat::to_json().
I did think a bit about it, and there's a way to achieve this with CRTP.
Here is a full working example:
#include <iostream>
#include <nlohmann/json.hpp>
using nlohmann::json;
template <typename T>
struct Base
{
int a{1};
};
template <typename T>
struct Derived : Base<Derived<T>>
{
int b{2};
};
struct Final : Derived<Final>
{
int c{3};
};
template <typename T>
void to_json(json& j, Base<T> const& b)
{
j["value"] = b.a;
::nlohmann::to_json(j["derived"], static_cast<T const&>(b));
}
template <typename T>
void to_json(json& j, Derived<T> const& d)
{
j["value"] = d.b;
::nlohmann::to_json(j["final"], static_cast<T const&>(d));
}
void to_json(json& j, Final const& f)
{
j["value"] = f.c;
}
template <typename T>
void print_json(Base<T> const& val)
{
json j = val;
std::cout << j.dump(2) << std::endl;
}
template <typename T>
void print_json(Derived<T> const& val)
{
print_json(static_cast<Base<Derived<T>> const&>(val));
}
template <typename T>
void print_json(T const& val)
{
print_json(static_cast<Derived<T> const&>(val));
}
int main(int argc, char const *argv[])
{
Final f;
print_json(f);
}
Note that:
Final f;
json j = f; // will only call to_json(json, Final);
j = static_cast<Derived<Final> const&>(f); // to_json(json, Derived<Final>);
j = static_cast<Base<Derived<Final>> const&>(f); //to_json(json, Base<Derived<Final> const&>);
I've hidden those static_cast in the print_json helper functions, I don't think you want your users to perform the casts themselves. Here the print_json will always call the full chain from Base to the lowest level.
The good thing here is that you don't have to rely on virtual functions, users still write to_json methods as usual.
Thank you @theodelrieu for the suggestion, and for introducing me to the “curiously recurring template pattern” of which I had never heard!
No problem :)
I take a look at your example. Whats the difference. You also have to know about the derived class in the base class
You have to know that something is derived from base.
template <typename T>
void to_json(json& j, Base<T> const& b)
{
j["value"] = b.a;
::nlohmann::to_json(j["derived"], static_cast<T const&>(b));
}
This is also working. Without knowing something about the derived class in base to_json method.
struct Base
{
int a{1};
};
struct Derived : Base
{
int b{2};
};
inline void to_json(json& j, const Base& data)
{
j = json
{
{ "a", data.a }
};
}
inline void to_json(json& j, const Derived& data)
{
j = json
{
static_cast<Base>(data),
{ "b", data.b }
};
}
But what about the from_json method:
I don't know any solution without a switch case in the from_json base class method.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@nlohmann the example you provided on Aug 28, 2017, Can you tell me how can I access the member of the class systembase?
With code
void from_json(const json& j, systembase& sb) {
sb.type = j.at("type");
sb.name = j.at("name");
}
you store a systembase value as an object. So with
systembase s;
s.name = "foo";
s.type = "bar";
json j = s;
j now holds this JSON value:
{"name": "foo", "type": "bar"}
What if I just have to read the value and not write it? As I'm receiving Json from other source and I just have to fill it in class.
Then you need to implement a to_json function, see https://github.com/nlohmann/json#basic-usage.
It is a little confusing. But I'll figure it out.
I have one question does it make any difference if I have a nested JSon? Do I have to add adl_serializer or just the to and from json?
Just to_json.
Can you tell me how can I write to_json if I have a nested json?
{
"MT": "C",
"C": [
{
"ID": 1,
"MD": 0,
}
{
"ID": 2,
"MD": 0,
}
]
}
How can I get the value of Id with value 2?
It was easy, this is how I did it
Json j = I had my entire json string here
json C = j["C"];
cout << C[1]["ID"]<< endl;
Amazing library, very easy to use and quick assistance than I every got on internet.
@ Nlohmann Thank you so much
Most helpful comment
I am not sure what you want to do exactly, but this may be helpful: