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..2b2e6ea0ce 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,34 @@ 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 +410,23 @@ 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..dab3e95ffc 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; }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a6b4c3a713..ae07da5469 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,34 @@ 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 +5916,23 @@ 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) {