open-source-parsers / jsoncpp

A C++ library for interacting with JSON.
Other
8.07k stars 2.63k forks source link

Serialization #1492

Open shishc9 opened 1 year ago

shishc9 commented 1 year ago

Hello, it's the first time I create an issue under an opensource. We use jsoncpp 0.5.0 version as a third lib. Recently I realize json to struct function base on jsoncpp's Json::Value. Here is my serializer.h.

namespace Json {

template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F&& f) {
    using unpack_t = int[];
    (void)unpack_t{(static_cast<void>(f(std::integral_constant<T, S>{})), 0)..., 0};
}

template<typename Class, typename T>
struct PropertyImpl {
  constexpr PropertyImpl(T Class::*aMember, const char* aName) : member{aMember}, name{aName} {}

  using Type = T;

  T Class::*member;
  const char* name;
};

template<typename Class, typename T>
constexpr auto property(T Class::*member, const char* name) {
  return PropertyImpl<Class, T>{member, name};
}

template<typename T>
struct PropertyTag {
  typedef char yes;
  typedef long no;
  template <typename TT>
  static auto CheckTag(int) -> decltype(std::declval<TT>().properties, yes());

  template <typename TT>
  static std::false_type CheckTag(...);
  static constexpr bool hasProperty = std::is_same<decltype(CheckTag<T>(0)), yes>::value;
};

template<typename T, typename std::enable_if<PropertyTag<T>::hasProperty, int>::type = 0>
T FromJson(const Json::Value& value);

template<typename T, typename std::enable_if<!PropertyTag<T>::hasProperty, int>::type = 0>
T FromJson(const Json::Value& value) {
    return T();
}

template<typename T, typename std::enable_if<PropertyTag<T>::hasProperty, int>::type = 0>
Json::Value ToJson(const T &object);

template<typename T, typename std::enable_if<!PropertyTag<T>::hasProperty, int>::type = 0>
Json::Value ToJson(const T &object) {
    return Json::Value();
}

template<typename T, typename std::enable_if<PropertyTag<T>::hasProperty, int>::type = 0> 
inline T asAny(const Json::Value& value) {
  return FromJson<T>(value);
}

template <typename T, typename std::enable_if<!PropertyTag<T>::hasProperty, int>::type = 0>
inline T asAny(const Json::Value& value) {
  return T{};
}

template<>
inline int asAny<int>(const Json::Value& value) {
  return value.asInt();
}

template<>
inline std::string asAny<std::string>(const Json::Value& value) {
  return value.asString();
}

template<>
inline char* asAny<char*>(const Json::Value& value) {
    if (value.type() != Json::stringValue) {
        return "";
    }
    return const_cast<char*>(value.asCString());
}

template<>
inline bool asAny<bool>(const Json::Value& value) {
  return value.asBool();
}

template<>
inline double asAny<double>(const Json::Value& value) {
  return value.asDouble();
}

template<>
inline float asAny<float>(const Json::Value& value) {
  return value.asDouble();
}

template<>
inline uint asAny<uint>(const Json::Value& value) {
  return value.asUInt();
}

template<>
inline std::vector<std::string> asAny<std::vector<std::string>>(const Json::Value& value) {
  std::vector<std::string> vec;
  if (value.isArray() && value.size() != 0) {
    for (uint i = 0; i < value.size(); ++i) {
      vec.push_back(value[i].asString());
    }
  }
  return vec;
}

template<>
inline std::vector<int> asAny<std::vector<int>>(const Json::Value& value) {
  std::vector<int> vec;
  if (value.isArray() && value.size() != 0) {
    for (uint i = 0; i < value.size(); ++i) {
      vec.push_back(value[i].asInt());
    }
  }
  return vec;
}

template <typename T, typename std::enable_if<PropertyTag<T>::hasProperty, int>::type = 0>
Json::Value convertJson(const T& object) {
  return ToJson(object);
}

template <typename T, typename std::enable_if<!PropertyTag<T>::hasProperty, int>::type = 0>
Json::Value convertJson(const T &object) {
  return Json::Value();
}

template<>
Json::Value convertJson<int>(const int& object) {
  return Json::Value(object);
}

template<>
Json::Value convertJson<uint>(const uint& object) {
  return Json::Value(object);
}

template<>
Json::Value convertJson<float>(const float& object) {
  return Json::Value(object);
}

template<>
Json::Value convertJson<double>(const double& object) {
  return Json::Value(object);
}

template<>
Json::Value convertJson<std::string>(const std::string& object) {
  return Json::Value(object);
}

template<>
Json::Value convertJson<char*>(char* const& object) {
    if (object == nullptr) {
        return Json::Value("");
    }
    return Json::Value(object);
}

template<>
Json::Value convertJson<std::vector<std::string>>(const std::vector<std::string>& object) {
  Json::Value data;
  for (auto str : object) {
    data.append(str);
  }
  return data;
}

template<>
Json::Value convertJson<std::vector<int>>(const std::vector<int>& object) {
  Json::Value data;
  for (auto str : object) {
    data.append(str);
  }
  return data;
}

template<typename T, typename std::enable_if<PropertyTag<T>::hasProperty, int>::type>
T FromJson(const Json::Value& data) {
  T object;

  constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;

  for_sequence(std::make_index_sequence<nbProperties>{}, [&](auto i){
    constexpr auto property = std::get<i>(T::properties);
    using Type = typename decltype(property)::Type;
    if (data.isMember(property.name)) {
        object.*(property.member) = std::move(asAny<Type>(data[property.name]));
    }
  });

  return object;
}

template<typename T, typename std::enable_if<PropertyTag<T>::hasProperty, int>::type>
Json::Value ToJson(const T& object) {
  Json::Value data;
  constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;
  for_sequence(std::make_index_sequence<nbProperties>{}, [&](auto i){
    constexpr auto property = std::get<i>(T::properties);
    using Type = typename decltype(property)::Type;
    data[property.name] = std::move(convertJson<Type>(object.*(property.member)));
  });

  return data;
}
}

it bases on c++14. I provide Json::ToJson() and Json::FromJson() two interface for serialization and deserialization. But It just support some basic type, std::vector\<std::string>, std::vector\<int>, and customized struct or class.

for example, InnerS2 has tuple type member named properties. It can be serialized.

struct InnerS2 {
  std::string m1;

  constexpr static auto properties = std::make_tuple(
    Json::property(&InnerS2::m1, "m1")
  );
};

What about your ideas ? I will seriously refer to your suggestions. thx.