C++ convert json to object
There is two solutions.
Do it manually
you can provide a function that takes a json::value
and return the object of your type:
User fromJson(json::value data) { return User{data[U("username")].as_string(), data[U("info")].as_string()};}
Do it automatically
There is no reflection in C++
. True. But if the compiler can't provide you with metadata, you can provide it yourself.
Let's start by making a propery struct:
template<typename Class, typename T>struct Property { constexpr Property(T Class::*aMember, const char* aName) : member{aMember}, name{aName} {} using Type = T; T Class::*member; const char* name;};
Ok, now we have the building block of our compile-time introspection system.
Now in your class user, add your metadata:
struct User { constexpr static auto properties = std::make_tuple( Property<User, std::string>{&User::username, "username"}, Property<User, std::string>{&User::info, "info"} );private: std::string username; std::string info;};
Now that you have the desired metadata, you can iterate through it by recursion:
template<std::size_t iteration, typename T>void doSetData(T&& object, const json::value& data) { // get the property constexpr auto property = std::get<iteration>(std::decay_t<T>::properties); // get the type of the property using Type = typename decltype(property)::Type; // set the value to the member object.*(property.member) = asAny<Type>(data[U(property.name)]);}template<std::size_t iteration, typename T, typename = std::enable_if_t<(iteration > 0)>>void setData(T&& object, const json::value& data) { doSetData<iteration>(object, data); // next iteration setData<iteration - 1>(object, data);}template<std::size_t iteration, typename T, typename = std::enable_if_t<(iteration == 0)>>void setData(T&& object, const json::value& data) { doSetData<iteration>(object, data);}template<typename T>T fromJson(Json::Value data) { T object; setData<std::tuple_size<decltype(T::properties)>::value - 1>(object, data); return object;}
That will do the trick.
I did not test this code, so if you have trouble, tell me in the comments.
Note that you will need to write the asAny
function. It's just a function that takes a Json::Value and call the right as_...
function, or another fromJson
;)
I have reworked the Guillaume solution to support c++11. A full working solution with some "polyfill" of c++14's decay_t and enable_if_t features to work with c++11 is below:
// main.cpp#include <iostream>#include <type_traits>#include <tuple>#include <jsoncpp/json/json.h>template<typename Class, typename T>struct Property{ constexpr Property(T Class::*aMember, const char *aName) : member{aMember}, name{aName} {} using Type = T; T Class::*member; const char *name;};class User{ std::string username; std::string info;public: constexpr static auto properties = std::make_tuple(Property<User, std::string>{&User::username, "username"}, Property<User, std::string>{&User::info, "info"}); const std::string &getUsername() const { return username; } void setUsername(const std::string &username) { User::username = username; } const std::string &getInfo() const { return info; } void setInfo(const std::string &info) { User::info = info; }};template< class T >using decay_t = typename std::decay<T>::type;template< bool B, class T = void >using enable_if_t = typename std::enable_if<B,T>::type;template<std::size_t iteration, typename T>void doSetData(T &&object, const Json::Value &data){ constexpr auto property = std::get<iteration>(decay_t<T>::properties); using Type = typename decltype(property)::Type; object.*(property.member) = data[property.name].asString();}template<std::size_t iteration, typename T, enable_if_t<(iteration > 0)>* = nullptr>void setData(T &&object, const Json::Value &data){ doSetData<iteration>(object, data); setData<iteration - 1>(object, data);}template<std::size_t iteration, typename T, enable_if_t<(iteration == 0)>* = nullptr>void setData(T &&object, const Json::Value &data){ doSetData<iteration>(object, data);}template<typename T>T fromJson(Json::Value data){ T object; setData<std::tuple_size<decltype(T::properties)>::value - 1>(object, data); return object;}int main(){ Json::Value value; value["username"] = "fiorentinoing"; value["info"] = "https://www.linkedin.com/in/fiorentinoing/"; User u = fromJson<User>(value); std::cout << "Hello, "<< u.getUsername() <<"!" << std::endl; std::cout << "Please, visit "<< u.getInfo() <<"." << std::endl; return 0;}
With libjsoncpp-dev as dependency, in order to build under Ubuntu 18.04 you can issue:
g++ --std=c++11 -o static_reflection main.cpp -ljsoncpp