diff --git a/README.md b/README.md index ca51fbb..4685d3d 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,9 @@ The following table shows the mapping of supported C++ types to supported JSON t * custom types must provide a default constructor. * constant member variables are skipped. * It is possible to treat custom types as set/map using the macro `REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH`, - `REFLECTIVE_RAPIDJSON_TREAT_AS_SET` or `REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_SET`. + `REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_MAP_OR_HASH`, `REFLECTIVE_RAPIDJSON_TREAT_AS_SET` or + `REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_SET`. +* The key type of the `std::map`, `std::unordered_map` must be `std::string`. * For custom (de)serialization, see the section below. * The binary (de)serializer supports approximately the same C++ types but obviously maps them to a platform independent binary representation rather than a JSON type. diff --git a/lib/binary/reflector.h b/lib/binary/reflector.h index ab34da1..79691ba 100644 --- a/lib/binary/reflector.h +++ b/lib/binary/reflector.h @@ -58,10 +58,10 @@ public: template > * = nullptr> void read(Type &pair); template > * = nullptr> void read(Type &pair); template , Traits::IsResizable> * = nullptr> void read(Type &iteratable); - template > * = nullptr> void read(Type &iteratable); + template , IsMultiMapOrHash> * = nullptr> void read(Type &iteratable); template , Traits::None, Traits::All, Traits::IsResizable>>> - * = nullptr> + Traits::EnableIf, + Traits::None, IsMultiMapOrHash, Traits::All, Traits::IsResizable>>> * = nullptr> void read(Type &iteratable); template > * = nullptr> void read(Type &customType); template > * = nullptr> void read(Type &customType); @@ -138,7 +138,7 @@ template , Traits::IsResizable> *> void BinaryDeserializer::read(Type &iteratable) +template , IsMultiMapOrHash> *> void BinaryDeserializer::read(Type &iteratable) { const auto size = readVariableLengthUIntBE(); for (size_t i = 0; i != size; ++i) { @@ -149,7 +149,8 @@ template > *> void BinaryDeser } template , Traits::None, Traits::All, Traits::IsResizable>>> *> + Traits::EnableIf, + Traits::None, IsMultiMapOrHash, Traits::All, Traits::IsResizable>>> *> void BinaryDeserializer::read(Type &iteratable) { const auto size = readVariableLengthUIntBE(); diff --git a/lib/json/reflector.h b/lib/json/reflector.h index 521a492..86282db 100644 --- a/lib/json/reflector.h +++ b/lib/json/reflector.h @@ -211,9 +211,10 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_ } /*! - * \brief Pushes the specified map (std::map, std::unordered_map) to the specified value. + * \brief Pushes the specified map (std::map, std::unordered_map) or multimap (std::multimap, std::unordered_multimap) to the + * specified value. */ -template > * = nullptr> +template , IsMultiMapOrHash> * = nullptr> void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { value.SetObject(); @@ -371,6 +372,12 @@ void pull(Type &reflectable, rapidjson::GenericValue> * = nullptr> void pull(Type &reflectable, const rapidjson::GenericValue> &value, JsonDeserializationErrors *errors); +/*! + * \brief Pulls the specified \a reflectable which is a multimap from the specified value which is checked to contain an object. + */ +template > * = nullptr> +void pull(Type &reflectable, const rapidjson::GenericValue> &value, JsonDeserializationErrors *errors); + /*! * \brief Pulls the specified \a reflectable which is a tuple from the specified value which is checked to contain an array. */ @@ -628,6 +635,25 @@ void pull(Type &reflectable, const rapidjson::GenericValue> *> +void pull(Type &reflectable, const rapidjson::GenericValue> &value, JsonDeserializationErrors *errors) +{ + if (!value.IsObject()) { + if (errors) { + errors->reportTypeMismatch(value.GetType()); + } + return; + } + auto obj = value.GetObject(); + for (auto i = obj.MemberBegin(), end = obj.MemberEnd(); i != end; ++i) { + auto insertedIterator = reflectable.insert(typename Type::value_type(i->name.GetString(), typename Type::mapped_type())); + pull(insertedIterator->second, i->value, errors); + } +} + namespace Detail { /*! @@ -762,7 +788,7 @@ void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue, IsMapOrHash> * = nullptr> +template , IsMapOrHash, IsMultiMapOrHash> * = nullptr> RAPIDJSON_NAMESPACE::StringBuffer toJson(const Type &reflectable) { RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); @@ -819,7 +845,7 @@ template > * = nullptr> RAPIDJSON_ /*! * \brief Deserializes the specified JSON to \tparam Type which is a custom type or can be mapped to an object. */ -template , IsMapOrHash> * = nullptr> +template , IsMapOrHash, IsMultiMapOrHash> * = nullptr> Type fromJson(const char *json, std::size_t jsonSize, JsonDeserializationErrors *errors = nullptr) { RAPIDJSON_NAMESPACE::Document doc(parseJsonDocFromString(json, jsonSize)); diff --git a/lib/tests/traits.cpp b/lib/tests/traits.cpp index b41bb4c..97a7bdb 100644 --- a/lib/tests/traits.cpp +++ b/lib/tests/traits.cpp @@ -12,6 +12,7 @@ struct Foo { struct Bar { }; REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH(Foo); +REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_MAP_OR_HASH(Foo); REFLECTIVE_RAPIDJSON_TREAT_AS_SET(Bar); REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_SET(Foo); @@ -37,6 +38,10 @@ static_assert(IsMapOrHash>::value, "map mapped to object"); static_assert(IsMapOrHash>::value, "hash mapped to object"); static_assert(!IsMapOrHash>::value, "vector not mapped to object"); static_assert(IsMapOrHash::value, "Foo mapped to object via TreatAsMapOrHash"); +static_assert(IsMultiMapOrHash>::value, "multimap mapped to object"); +static_assert(IsMultiMapOrHash>::value, "unordered multimap mapped to object"); +static_assert(!IsMultiMapOrHash>::value, "vector not mapped to object"); +static_assert(IsMultiMapOrHash::value, "Foo mapped to object via TreatAsMultiMapOrHash"); static_assert(IsIteratableExceptString>::value, "vector is iteratable"); static_assert(!IsIteratableExceptString::value, "string not iteratable"); static_assert(!IsIteratableExceptString::value, "wstring not iteratable"); diff --git a/lib/traits.h b/lib/traits.h index db7c629..681e37a 100644 --- a/lib/traits.h +++ b/lib/traits.h @@ -15,6 +15,9 @@ namespace ReflectiveRapidJSON { /// \brief \brief The TreatAsMapOrHash class allows treating custom classes as std::map or std::unordered_map. template struct TreatAsMapOrHash : public Traits::Bool { }; +/// \brief \brief The TreatAsMultiMapOrHash class allows treating custom classes as std::multimap or std::unordered_multimap. +template struct TreatAsMultiMapOrHash : public Traits::Bool { +}; /// \brief \brief The TreatAsSet class allows treating custom classes as std::set or std::unordered_set. template struct TreatAsSet : public Traits::Bool { }; @@ -25,6 +28,9 @@ template struct TreatAsMultiSet : public Traits::Bool { #define REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH(T) \ template <> struct TreatAsMapOrHash : public Traits::Bool { \ } +#define REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_MAP_OR_HASH(T) \ + template <> struct TreatAsMultiMapOrHash : public Traits::Bool { \ + } #define REFLECTIVE_RAPIDJSON_TREAT_AS_SET(T) \ template <> struct TreatAsSet : public Traits::Bool { \ } @@ -37,6 +43,9 @@ template using IsMapOrHash = Traits::Any, Traits::IsSpecializationOf, TreatAsMapOrHash>; template +using IsMultiMapOrHash = Traits::Any, Traits::IsSpecializationOf, + TreatAsMultiMapOrHash>; +template using IsSet = Traits::Any, Traits::IsSpecializationOf, TreatAsSet>; template using IsMultiSet