From 340831ea54d0fffd63e54d1a8183ac9754e3c9b3 Mon Sep 17 00:00:00 2001 From: Muhammad Amir bin Mohamad Ghazaly Date: Fri, 6 Dec 2024 23:33:02 -0500 Subject: [PATCH 1/3] First working solution --- .../nlohmann/detail/conversions/from_json.hpp | 25 +++- .../nlohmann/detail/conversions/to_json.hpp | 40 ++++++- include/nlohmann/detail/macro_scope.hpp | 40 +++++++ include/nlohmann/detail/meta/type_traits.hpp | 8 +- single_include/nlohmann/json.hpp | 111 ++++++++++++++++-- 5 files changed, 209 insertions(+), 15 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 8f83341f48..06b0642070 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -325,8 +325,9 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin = *j.template get_ptr(); } -template::value, int> = 0> +template < typename BasicJsonType, typename ConstructibleObjectType, + enable_if_t < is_constructible_object_type::value&& + !std::is_enum::value, int > = 0 > inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) @@ -347,6 +348,23 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) obj = std::move(ret); } +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + enable_if_t < is_constructible_object_type>::value&& + is_compatible_object_type>::value&& + std::is_enum::value, int > = 0 > +inline void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); + } + m.clear(); + for (const auto& p : j.items()) + { + m.emplace(string_to_enum(json(p.key()), Key()), p.value().template get()); + } +} + // overload for arithmetic types, not chosen for basic_json template arguments // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not @@ -440,7 +458,8 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, typename = enable_if_t < !std::is_constructible < - typename BasicJsonType::string_t, Key >::value >> + typename BasicJsonType::string_t, Key >::value && + !is_compatible_object_type>::value >> inline void from_json(const BasicJsonType& j, std::map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 18c4493e75..30aed02e51 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -19,6 +19,7 @@ #include // move, forward, declval, pair #include // valarray #include // vector +#include // map #include #include @@ -244,8 +245,33 @@ struct external_constructor j.assert_invariant(); } + template < typename BasicJsonType, typename Key, typename Value, + enable_if_t < is_compatible_object_type>::value&& + !is_basic_json>::value&& + std::is_enum::value, int > = 0 > + static void construct(BasicJsonType& j, const std::map& obj) + { + using std::begin; + using std::end; + std::map temp; + for (auto& i : obj) + { + Key first = i.first; + Value second = i.second; + temp.insert({ enum_to_string(j, first), second }); + } + + j.m_data.m_value.destroy(j.m_data.m_type); + j.m_data.m_type = value_t::object; + j.m_data.m_value.object = j.template create(begin(temp), end(temp)); + j.set_parents(); + j.assert_invariant(); + } + template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < !std::is_same::value, int > = 0 > + enable_if_t < !std::is_same::value&& + is_compatible_object_type::value&& !is_basic_json::value&& + !std::is_enum::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; @@ -383,12 +409,22 @@ inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) } template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value&& + !std::is_enum::value, int > = 0 > inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor::construct(j, obj); } +template < typename BasicJsonType, typename Key, typename Value, + enable_if_t < (is_compatible_object_type>::value)&& + (!is_basic_json>::value)&& + std::is_enum::value, int > = 0 > +inline void to_json(BasicJsonType& j, const std::map& obj) +{ + external_constructor::construct(j, obj); +} + template inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 424a82943b..a5c88b2fdf 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -240,7 +240,47 @@ return ej_pair.second == j; \ }); \ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } \ + /* Function to check for serialized ENUM type */ \ + template \ + inline constexpr bool serialized(BasicJsonType& j, ENUM_TYPE e) \ + { \ + return true; \ + } \ + template \ + inline std::string enum_to_string(BasicJsonType j, ENUM_TYPE e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + return ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline ENUM_TYPE string_to_enum(BasicJsonType j, ENUM_TYPE e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + return ((it != std::end(m)) ? it : std::begin(m))->first; \ } +// Function to check for non-serialized ENUM type +template +inline constexpr bool serialized(BasicJsonType& j, EnumType e) +{ + return false; +} // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 4a7b3105b8..ccef9e0624 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -357,8 +357,10 @@ struct is_compatible_object_type_impl < // macOS's is_constructible does not play well with nonesuch... static constexpr bool value = - is_constructible::value && + (is_constructible::value || + (std::is_enum::value && + serialized("", typename CompatibleObjectType::key_type()))) && is_constructible::value; }; @@ -384,7 +386,7 @@ struct is_constructible_object_type_impl < (std::is_move_assignable::value || std::is_copy_assignable::value) && (is_constructible::value && + typename object_t::key_type>::value && std::is_same < typename object_t::mapped_type, typename ConstructibleObjectType::mapped_type >::value)) || diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a6b4c3a713..bdce0b7ee6 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2596,7 +2596,47 @@ JSON_HEDLEY_DIAGNOSTIC_POP return ej_pair.second == j; \ }); \ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } \ + /* Function to check for serialized ENUM type */ \ + template \ + inline constexpr bool serialized(BasicJsonType& j, ENUM_TYPE e) \ + { \ + return true; \ + } \ + template \ + inline std::string enum_to_string(BasicJsonType j, ENUM_TYPE e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + return ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline ENUM_TYPE string_to_enum(BasicJsonType j, ENUM_TYPE e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + return ((it != std::end(m)) ? it : std::begin(m))->first; \ } +// Function to check for non-serialized ENUM type +template +inline constexpr bool serialized(BasicJsonType& j, EnumType e) +{ + return false; +} // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. @@ -3804,8 +3844,10 @@ struct is_compatible_object_type_impl < // macOS's is_constructible does not play well with nonesuch... static constexpr bool value = - is_constructible::value && + (is_constructible::value || + (std::is_enum::value && + serialized("", typename CompatibleObjectType::key_type()))) && is_constructible::value; }; @@ -4998,8 +5040,9 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin = *j.template get_ptr(); } -template::value, int> = 0> +template < typename BasicJsonType, typename ConstructibleObjectType, + enable_if_t < is_constructible_object_type::value&& + !std::is_enum::value, int > = 0 > inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) @@ -5020,6 +5063,23 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) obj = std::move(ret); } +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + enable_if_t < is_constructible_object_type>::value&& + is_compatible_object_type>::value&& + std::is_enum::value, int > = 0 > +inline void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); + } + m.clear(); + for (const auto& p : j.items()) + { + m.emplace(string_to_enum(json(p.key()), Key()), p.value().template get()); + } +} + // overload for arithmetic types, not chosen for basic_json template arguments // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not @@ -5113,7 +5173,8 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, typename = enable_if_t < !std::is_constructible < - typename BasicJsonType::string_t, Key >::value >> + typename BasicJsonType::string_t, Key >::value && + !is_compatible_object_type>::value >> inline void from_json(const BasicJsonType& j, std::map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) @@ -5213,6 +5274,7 @@ NLOHMANN_JSON_NAMESPACE_END #include // move, forward, declval, pair #include // valarray #include // vector +#include // map // #include // __ _____ _____ _____ @@ -5689,8 +5751,33 @@ struct external_constructor j.assert_invariant(); } + template < typename BasicJsonType, typename Key, typename Value, + enable_if_t < is_compatible_object_type>::value&& + !is_basic_json>::value&& + std::is_enum::value, int > = 0 > + static void construct(BasicJsonType& j, const std::map& obj) + { + using std::begin; + using std::end; + std::map temp; + for (auto& i : obj) + { + Key first = i.first; + Value second = i.second; + temp.insert({ enum_to_string(j, first), second }); + } + + j.m_data.m_value.destroy(j.m_data.m_type); + j.m_data.m_type = value_t::object; + j.m_data.m_value.object = j.template create(begin(temp), end(temp)); + j.set_parents(); + j.assert_invariant(); + } + template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < !std::is_same::value, int > = 0 > + enable_if_t < !std::is_same::value&& + is_compatible_object_type::value&& !is_basic_json::value&& + !std::is_enum::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; @@ -5828,12 +5915,22 @@ inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) } template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value&& + !std::is_enum::value, int > = 0 > inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor::construct(j, obj); } +template < typename BasicJsonType, typename Key, typename Value, + enable_if_t < (is_compatible_object_type>::value)&& + (!is_basic_json>::value)&& + std::is_enum::value, int > = 0 > +inline void to_json(BasicJsonType& j, const std::map& obj) +{ + external_constructor::construct(j, obj); +} + template inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { From 56f17665f1f4968a492d2c17a76ced9f5a4ca757 Mon Sep 17 00:00:00 2001 From: Muhammad Amir bin Mohamad Ghazaly Date: Fri, 6 Dec 2024 23:49:32 -0500 Subject: [PATCH 2/3] Tidying up the code --- .../nlohmann/detail/conversions/from_json.hpp | 18 +++++++++--------- .../nlohmann/detail/conversions/to_json.hpp | 10 ++++++---- include/nlohmann/detail/meta/type_traits.hpp | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 06b0642070..e7f3cdd1ae 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -325,9 +325,9 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin = *j.template get_ptr(); } -template < typename BasicJsonType, typename ConstructibleObjectType, - enable_if_t < is_constructible_object_type::value&& - !std::is_enum::value, int > = 0 > +template::value&& + !std::is_enum::value, int> = 0> inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) @@ -348,10 +348,10 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) obj = std::move(ret); } -template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, - enable_if_t < is_constructible_object_type>::value&& - is_compatible_object_type>::value&& - std::is_enum::value, int > = 0 > +template >::value&& + is_compatible_object_type>::value&& + std::is_enum::value, int> = 0> inline void from_json(const BasicJsonType& j, std::map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) @@ -361,7 +361,7 @@ inline void from_json(const BasicJsonType& j, std::map()); + m.emplace(string_to_enum(json(p.key()),Key()), p.value().template get()); } } @@ -459,7 +459,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, typename = enable_if_t < !std::is_constructible < typename BasicJsonType::string_t, Key >::value && - !is_compatible_object_type>::value >> + !is_compatible_object_type>::value>> inline void from_json(const BasicJsonType& j, std::map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 30aed02e51..2b2e6ea0ce 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -270,7 +270,8 @@ struct external_constructor template < typename BasicJsonType, typename CompatibleObjectType, enable_if_t < !std::is_same::value&& - is_compatible_object_type::value&& !is_basic_json::value&& + is_compatible_object_type::value&& + !is_basic_json::value&& !std::is_enum::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { @@ -409,7 +410,8 @@ inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) } template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < is_compatible_object_type::value&& !is_basic_json::value&& + enable_if_t < is_compatible_object_type::value&& + !is_basic_json::value&& !std::is_enum::value, int > = 0 > inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { @@ -417,8 +419,8 @@ inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj) } template < typename BasicJsonType, typename Key, typename Value, - enable_if_t < (is_compatible_object_type>::value)&& - (!is_basic_json>::value)&& + enable_if_t < is_compatible_object_type>::value&& + !is_basic_json>::value&& std::is_enum::value, int > = 0 > inline void to_json(BasicJsonType& j, const std::map& obj) { diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index ccef9e0624..dab3e95ffc 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -386,7 +386,7 @@ struct is_constructible_object_type_impl < (std::is_move_assignable::value || std::is_copy_assignable::value) && (is_constructible::value && + typename object_t::key_type>::value && std::is_same < typename object_t::mapped_type, typename ConstructibleObjectType::mapped_type >::value)) || From f26bec833c4062bce34ac61c7e665d85ed65a966 Mon Sep 17 00:00:00 2001 From: Muhammad Amir bin Mohamad Ghazaly Date: Fri, 6 Dec 2024 23:53:18 -0500 Subject: [PATCH 3/3] Add the amalgamated files --- .../nlohmann/detail/conversions/from_json.hpp | 18 +++++++++--------- single_include/nlohmann/json.hpp | 10 ++++++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index e7f3cdd1ae..06b0642070 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -325,9 +325,9 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin = *j.template get_ptr(); } -template::value&& - !std::is_enum::value, int> = 0> +template < typename BasicJsonType, typename ConstructibleObjectType, + enable_if_t < is_constructible_object_type::value&& + !std::is_enum::value, int > = 0 > inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) @@ -348,10 +348,10 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) obj = std::move(ret); } -template >::value&& - is_compatible_object_type>::value&& - std::is_enum::value, int> = 0> +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + enable_if_t < is_constructible_object_type>::value&& + is_compatible_object_type>::value&& + std::is_enum::value, int > = 0 > inline void from_json(const BasicJsonType& j, std::map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) @@ -361,7 +361,7 @@ inline void from_json(const BasicJsonType& j, std::map()); + m.emplace(string_to_enum(json(p.key()), Key()), p.value().template get()); } } @@ -459,7 +459,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, typename = enable_if_t < !std::is_constructible < typename BasicJsonType::string_t, Key >::value && - !is_compatible_object_type>::value>> + !is_compatible_object_type>::value >> inline void from_json(const BasicJsonType& j, std::map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index bdce0b7ee6..ae07da5469 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5776,7 +5776,8 @@ struct external_constructor template < typename BasicJsonType, typename CompatibleObjectType, enable_if_t < !std::is_same::value&& - is_compatible_object_type::value&& !is_basic_json::value&& + is_compatible_object_type::value&& + !is_basic_json::value&& !std::is_enum::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { @@ -5915,7 +5916,8 @@ inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) } template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < is_compatible_object_type::value&& !is_basic_json::value&& + enable_if_t < is_compatible_object_type::value&& + !is_basic_json::value&& !std::is_enum::value, int > = 0 > inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { @@ -5923,8 +5925,8 @@ inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj) } template < typename BasicJsonType, typename Key, typename Value, - enable_if_t < (is_compatible_object_type>::value)&& - (!is_basic_json>::value)&& + enable_if_t < is_compatible_object_type>::value&& + !is_basic_json>::value&& std::is_enum::value, int > = 0 > inline void to_json(BasicJsonType& j, const std::map& obj) {