diff --git a/README.md b/README.md index 47335b3..ecac532 100644 --- a/README.md +++ b/README.md @@ -266,11 +266,13 @@ void test_jsonization() { std::vector vec; std::map map; - int i = 0; double d = 0; // 如果你正在使用 MSVC, 请添加 "/Zc:preprocessor" 到项目设置中 + + // MEO_OPT 表示该变量是一个可选项 + // 即使输入中不存在该字段依然可以读取 MEO_JSONIZATION(vec, map, MEO_OPT i, d); }; @@ -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); diff --git a/README_en.md b/README_en.md index 237060f..d187584 100644 --- a/README_en.md +++ b/README_en.md @@ -266,11 +266,13 @@ void test_jsonization() { std::vector vec; std::map 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); }; @@ -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); diff --git a/include/json.hpp b/include/json.hpp index 2106ec1..6fa706d 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -2788,48 +2788,121 @@ struct next_is_optional_t struct va_arg_end {}; +template +class has_jsonization_in_member +{ + template + static auto test(int) -> decltype(std::declval().to_json(), + std::declval().from_json(std::declval()), std::true_type()); + + template + static std::false_type test(...); + +public: + static constexpr bool value = decltype(test(0))::value; +}; + +template +class has_jsonization_in_global +{ + template + static auto test(int) -> decltype(to_json(std::declval()), + from_json(std::declval(), std::declval()), std::true_type()); + + template + static std::false_type test(...); + +public: + static constexpr bool value = decltype(test(0))::value; +}; + +struct string_converter +{ + template + static constexpr bool is_convertible = + has_jsonization_in_member::value || has_jsonization_in_global::value; + + template + auto operator()(input_t&& arg) const + { + if constexpr (has_jsonization_in_member::value) { + return std::forward(arg).to_json(); + } + else if constexpr (has_jsonization_in_global::value) { + return to_json(std::forward(arg)); + } + else { + static_assert(!sizeof(input_t), "Unable to convert"); + } + } +}; + class dumper { public: template - 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)...); - if constexpr (!std::is_same_v) { - result.emplace(key, json::serialize(var)); + json::object result = _to_json(key, std::forward(rest)...); + + if constexpr (has_jsonization_in_member::value) { + result.emplace(key, var.to_json()); + } + else if constexpr (has_jsonization_in_global::value) { + result.emplace(key, to_json(var)); + } + else if constexpr (!std::is_same_v) { + result.emplace(key, json::serialize(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 - 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()) { - error_key = key; - return false; + if constexpr (has_jsonization_in_member::value) { + if (!opt->is_object() || !var.from_json(opt->as_object())) { + error_key = key; + return false; + } + } + else if constexpr (has_jsonization_in_global::value) { + if (!opt->is_object() || !from_json(opt->as_object(), var)) { + error_key = key; + return false; + } + } + else { + if (!opt->is()) { + error_key = key; + return false; + } + var = std::move(opt)->as(); } - var = std::move(opt)->as(); _next_is_optional = false; } else if (!_next_is_optional) { error_key = key; return false; } - return from_json(in, error_key, std::forward(rest)...); + return _from_json(in, error_key, std::forward(rest)...); } template - 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)...); + return _from_json(in, error_key, std::forward(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; @@ -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 {}, diff --git a/sample/sample.cpp b/sample/sample.cpp index 6fa4622..455759c 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -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 vec; std::map 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; @@ -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; }