Skip to content

Commit

Permalink
feat: has_jsonization
Browse files Browse the repository at this point in the history
  • Loading branch information
MistEO committed Jan 24, 2024
1 parent dbf70c3 commit 797ea3a
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 47 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,13 @@ void test_jsonization()
{
std::vector<int> vec;
std::map<std::string, int> map;

int i = 0;
double d = 0;

// 如果你正在使用 MSVC, 请添加 "/Zc:preprocessor" 到项目设置中
// MEO_OPT 表示该变量是一个可选项
// 即使输入中不存在该字段依然可以读取
MEO_JSONIZATION(vec, map, MEO_OPT i, d);
};

Expand All @@ -285,13 +287,12 @@ void test_jsonization()
// output: { "d" : 0.500000, "i" : 100, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] }
std::cout << dumps << std::endl;

// MEO_OPT 表示该变量是一个可选项
// 即使输入中不存在该字段依然可以读取
dumps.erase("i")

// output: { "d" : 0.500000, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] }
std::cout << dumps << std::endl;

// MEO_OPT 表示该变量是一个可选项
// 即使输入中不存在该字段依然可以读取
MyStruct b;
b.from_json(dumps);

Expand Down
9 changes: 5 additions & 4 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,13 @@ void test_jsonization()
{
std::vector<int> vec;
std::map<std::string, int> map;

int i = 0;
double d = 0;

// if you are using MSVC, please add "/Zc:preprocessor" to your project
// MEO_OPT means the var is optional
// and can still be read even if the field doesn't exist in the input.
MEO_JSONIZATION(vec, map, MEO_OPT i, d);
};

Expand All @@ -285,13 +287,12 @@ void test_jsonization()
// output: { "d" : 0.500000, "i" : 100, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] }
std::cout << dumps << std::endl;

// MEO_OPT means the var is optional
// and can still be read even if the field doesn't exist in the input.
dumps.erase("i")

// output: { "d" : 0.500000, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] }
std::cout << dumps << std::endl;

// MEO_OPT means the var is optional
// and can still be read even if the field doesn't exist in the input.
MyStruct b;
b.from_json(dumps);

Expand Down
133 changes: 103 additions & 30 deletions include/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2788,48 +2788,121 @@ struct next_is_optional_t
struct va_arg_end
{};

template <typename T>
class has_jsonization_in_member
{
template <typename U>
static auto test(int) -> decltype(std::declval<U>().to_json(),
std::declval<U>().from_json(std::declval<json::object>()), std::true_type());

template <typename U>
static std::false_type test(...);

public:
static constexpr bool value = decltype(test<T>(0))::value;
};

template <typename T>
class has_jsonization_in_global
{
template <typename U>
static auto test(int) -> decltype(to_json(std::declval<U>()),
from_json(std::declval<json::object>(), std::declval<U&>()), std::true_type());

template <typename U>
static std::false_type test(...);

public:
static constexpr bool value = decltype(test<T>(0))::value;
};

struct string_converter
{
template <typename input_t>
static constexpr bool is_convertible =
has_jsonization_in_member<input_t>::value || has_jsonization_in_global<input_t>::value;

template <typename input_t>
auto operator()(input_t&& arg) const
{
if constexpr (has_jsonization_in_member<input_t>::value) {
return std::forward<input_t>(arg).to_json();
}
else if constexpr (has_jsonization_in_global<input_t>::value) {
return to_json(std::forward<input_t>(arg));
}
else {
static_assert(!sizeof(input_t), "Unable to convert");
}
}
};

class dumper
{
public:
template <typename var_t, typename... rest_t>
json::object to_json(const char* key, const var_t& var, rest_t&&... rest) const
json::object _to_json(const char* key, const var_t& var, rest_t&&... rest) const
{
json::object result = to_json(std::forward<rest_t>(rest)...);
if constexpr (!std::is_same_v<next_is_optional_t, var_t>) {
result.emplace(key, json::serialize<false>(var));
json::object result = _to_json(key, std::forward<rest_t>(rest)...);

if constexpr (has_jsonization_in_member<var_t>::value) {
result.emplace(key, var.to_json());
}
else if constexpr (has_jsonization_in_global<var_t>::value) {
result.emplace(key, to_json(var));
}
else if constexpr (!std::is_same_v<next_is_optional_t, var_t>) {
result.emplace(key, json::serialize<false>(var, _string_converter));
}
return result;
}
json::object to_json(va_arg_end) const { return {}; }
json::object _to_json(const char*, va_arg_end) const { return {}; }

private:
string_converter _string_converter {};
};

class loader
{
public:
template <typename var_t, typename... rest_t>
bool from_json(const json::object& in, std::string& error_key, const char* key, var_t& var, rest_t&&... rest)
bool _from_json(const json::object& in, std::string& error_key, const char* key, var_t& var, rest_t&&... rest)
{
if (auto opt = in.find(key)) {
if (!opt->is<var_t>()) {
error_key = key;
return false;
if constexpr (has_jsonization_in_member<var_t>::value) {
if (!opt->is_object() || !var.from_json(opt->as_object())) {
error_key = key;
return false;
}
}
else if constexpr (has_jsonization_in_global<var_t>::value) {
if (!opt->is_object() || !from_json(opt->as_object(), var)) {
error_key = key;
return false;
}
}
else {
if (!opt->is<var_t>()) {
error_key = key;
return false;
}
var = std::move(opt)->as<var_t>();
}
var = std::move(opt)->as<var_t>();
_next_is_optional = false;
}
else if (!_next_is_optional) {
error_key = key;
return false;
}
return from_json(in, error_key, std::forward<rest_t>(rest)...);
return _from_json(in, error_key, std::forward<rest_t>(rest)...);
}
template <typename... rest_t>
bool from_json(const json::object& in, std::string& error_key, const char*, next_is_optional_t, rest_t&&... rest)
bool _from_json(const json::object& in, std::string& error_key, const char*, next_is_optional_t, rest_t&&... rest)
{
_next_is_optional = true;
return from_json(in, error_key, std::forward<rest_t>(rest)...);
return _from_json(in, error_key, std::forward<rest_t>(rest)...);
}
bool from_json(const json::object&, std::string&, va_arg_end) { return true; }
bool _from_json(const json::object&, std::string&, va_arg_end) { return true; }

private:
bool _next_is_optional = false;
Expand Down Expand Up @@ -2930,22 +3003,22 @@ namespace json::_private_macro
} // namespace json::_private_macro

// if you are using MSVC, please add "/Zc:preprocessor" to your project
#define MEO_JSONIZATION(...) \
json::object to_json() const \
{ \
return json::_jsonization_helper::dumper().to_json(_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \
json::_jsonization_helper::va_arg_end()); \
} \
bool from_json(const json::object& _MEOJSON_VARNAME(in)) \
{ \
std::string _MEOJSON_VARNAME(error_key); \
return from_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key)); \
} \
bool from_json(const json::object& _MEOJSON_VARNAME(in), std::string& _MEOJSON_VARNAME(error_key)) \
{ \
return json::_jsonization_helper::loader().from_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key), \
_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \
json::_jsonization_helper::va_arg_end()); \
#define MEO_JSONIZATION(...) \
json::object to_json() const \
{ \
return json::_jsonization_helper::dumper()._to_json(_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \
json::_jsonization_helper::va_arg_end()); \
} \
bool from_json(const json::object& _MEOJSON_VARNAME(in)) \
{ \
std::string _MEOJSON_VARNAME(error_key); \
return from_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key)); \
} \
bool from_json(const json::object& _MEOJSON_VARNAME(in), std::string& _MEOJSON_VARNAME(error_key)) \
{ \
return json::_jsonization_helper::loader()._from_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key), \
_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \
json::_jsonization_helper::va_arg_end()); \
}

#define MEO_OPT json::_jsonization_helper::next_is_optional_t {},
41 changes: 32 additions & 9 deletions sample/sample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,27 +297,50 @@ bool serializing()
return true;
}

void test_jsonization()
struct StructureFromOtherLibrary // like cv::Mat
{
struct AAA
{
int a_i = 100;
int a_i = 100;
};
json::object to_json(const StructureFromOtherLibrary&)
{
return { { "aaa", "ai" } };
}
bool from_json(const json::object&, StructureFromOtherLibrary& aa)
{
aa.a_i = -1;
return true;
}

MEO_JSONIZATION(a_i);
};
struct MyCustomStructure
{
int b_i = 10;

json::object to_json() const { return { { "bbb", "bi" } }; }
bool from_json(const json::object& in)
{
std::ignore = in;
b_i = -100;
return true;
}
};

void test_jsonization()
{
struct MyStruct
{
std::vector<int> vec;
std::map<std::string, int> map;

int i = 0;
double d = 0;

MEO_JSONIZATION(vec, map, MEO_OPT i, MEO_OPT d);
StructureFromOtherLibrary structureFromOtherLibrary;
MyCustomStructure myCustomStructure;

MEO_JSONIZATION(vec, map, MEO_OPT i, MEO_OPT d, structureFromOtherLibrary, myCustomStructure);
};

MyStruct a;

a.vec = { 1, 2, 3 };
a.map = { { "key", 5 } };
a.i = 100;
Expand All @@ -333,5 +356,5 @@ void test_jsonization()
bool loaded = b.from_json(j);

std::cout << "loaded: " << loaded << std::endl;
std::cout << b.to_json() << std::endl;
std::cout << b.myCustomStructure.b_i << std::endl;
}

0 comments on commit 797ea3a

Please sign in to comment.