From e9324f0ec31d4b861b40f41eea05db91672d55eb Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 12 Nov 2017 00:44:47 +0100 Subject: [PATCH] Fix incomplete use of AdaptedJsonSerializable and handling tuple --- generator/tests/jsongenerator.cpp | 30 +++++++++++++--- generator/tests/structs.h | 16 +++++++-- lib/json/reflector.h | 60 +++++++++++++++++-------------- 3 files changed, 73 insertions(+), 33 deletions(-) diff --git a/generator/tests/jsongenerator.cpp b/generator/tests/jsongenerator.cpp index ef4da96..ebb2a00 100644 --- a/generator/tests/jsongenerator.cpp +++ b/generator/tests/jsongenerator.cpp @@ -220,21 +220,41 @@ void JsonGeneratorTests::testCustomSerialization() */ void JsonGeneratorTests::test3rdPartyAdaption() { + // test whether specializations of AdaptedJsonSerializable are generated static_assert( ReflectiveRapidJSON::AdaptedJsonSerializable::value, "can serialize NotJsonSerializable because of adaption macro"); + static_assert(ReflectiveRapidJSON::AdaptedJsonSerializable::value, + "can serialize NestedNotJsonSerializable because of adaption macro"); static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable::value, "can not serialize OtherNotJsonSerializable because adaption macro missing"); static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable::value, "can not serialize ReallyNotJsonSerializable"); - const NotJsonSerializable test; - const string str("{\"butSerializableAnyways\":\"useful to adapt 3rd party structs\"}"); + const NotJsonSerializable simple; + const string strSimple("{\"butSerializableAnyways\":\"useful to adapt 3rd party structs\"}"); + const NestedNotJsonSerializable nested{ { "foo" }, { { "1" }, { "2" }, { "3" } }, { 42, { "bar" } } }; + const string strNested("{\"asMember\":{\"butSerializableAnyways\":\"foo\"},\"asArrayElement\":[{\"butSerializableAnyways\":\"1\"},{" + "\"butSerializableAnyways\":\"2\"},{\"butSerializableAnyways\":\"3\"}],\"asTupleElement\":[42,{\"butSerializableAnyways\":" + "\"bar\"}]}"); // test serialization - CPPUNIT_ASSERT_EQUAL(str, string(ReflectiveRapidJSON::JsonReflector::toJson(test).GetString())); + CPPUNIT_ASSERT_EQUAL(strSimple, string(ReflectiveRapidJSON::JsonReflector::toJson(simple).GetString())); + CPPUNIT_ASSERT_EQUAL(strNested, string(ReflectiveRapidJSON::JsonReflector::toJson(nested).GetString())); // test deserialization - const NotJsonSerializable parsedTest(ReflectiveRapidJSON::JsonReflector::fromJson(str)); - CPPUNIT_ASSERT_EQUAL(test.butSerializableAnyways, parsedTest.butSerializableAnyways); + JsonDeserializationErrors errors; + const auto parsedSimple(ReflectiveRapidJSON::JsonReflector::fromJson(strSimple, &errors)); + CPPUNIT_ASSERT_EQUAL(0_st, errors.size()); + CPPUNIT_ASSERT_EQUAL(simple.butSerializableAnyways, parsedSimple.butSerializableAnyways); + const auto parsedNested(ReflectiveRapidJSON::JsonReflector::fromJson(strNested, &errors)); + CPPUNIT_ASSERT_EQUAL(0_st, errors.size()); + CPPUNIT_ASSERT_EQUAL(nested.asMember.butSerializableAnyways, parsedNested.asMember.butSerializableAnyways); + CPPUNIT_ASSERT_EQUAL(nested.asMember.butSerializableAnyways, parsedNested.asMember.butSerializableAnyways); + CPPUNIT_ASSERT_EQUAL(nested.asArrayElement.size(), parsedNested.asArrayElement.size()); + CPPUNIT_ASSERT_EQUAL(nested.asArrayElement.at(0).butSerializableAnyways, parsedNested.asArrayElement.at(0).butSerializableAnyways); + CPPUNIT_ASSERT_EQUAL(nested.asArrayElement.at(1).butSerializableAnyways, parsedNested.asArrayElement.at(1).butSerializableAnyways); + CPPUNIT_ASSERT_EQUAL(nested.asArrayElement.at(2).butSerializableAnyways, parsedNested.asArrayElement.at(2).butSerializableAnyways); + CPPUNIT_ASSERT_EQUAL(get<0>(nested.asTupleElement), get<0>(parsedNested.asTupleElement)); + CPPUNIT_ASSERT_EQUAL(get<1>(nested.asTupleElement).butSerializableAnyways, get<1>(parsedNested.asTupleElement).butSerializableAnyways); } // include file required for reflection of TestStruct and other structs defined in structs.h diff --git a/generator/tests/structs.h b/generator/tests/structs.h index 80d8dfe..bdd3ca7 100644 --- a/generator/tests/structs.h +++ b/generator/tests/structs.h @@ -88,7 +88,7 @@ struct StructWithCustomTypes : public JsonSerializable { }; /*! - * \brief The NotJsonSerializable struct is used to tests (de)serialization for 3rd party structs (which do not + * \brief The NotJsonSerializable struct is used to test (de)serialization for 3rd party structs (which do not * inherit from JsonSerializable instance). It is used in JsonGeneratorTests::test3rdPartyAdaption(). * \remarks Imagine this struct would have been defined in a 3rd party header. */ @@ -96,8 +96,20 @@ struct NotJsonSerializable { std::string butSerializableAnyways = "useful to adapt 3rd party structs"; }; -// make "NotJsonSerializable" serializable +/*! + * \brief The NestedNotJsonSerializable struct is used to test (de)serialization for 3rd party structs (which do not + * inherit from JsonSerializable instance). It is used in JsonGeneratorTests::test3rdPartyAdaption(). + * \remarks Imagine this struct would have been defined in a 3rd party header. + */ +struct NestedNotJsonSerializable { + NotJsonSerializable asMember; + vector asArrayElement; + tuple asTupleElement; +}; + +// make "NotJsonSerializable" and "NestedNotJsonSerializable" serializable REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE(NotJsonSerializable); +REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE(NestedNotJsonSerializable); /*! * \brief The OtherNotJsonSerializable struct is used to test whether code for (de)serialization is generated for classes explicitely diff --git a/lib/json/reflector.h b/lib/json/reflector.h index 1b7b574..7b36027 100644 --- a/lib/json/reflector.h +++ b/lib/json/reflector.h @@ -77,26 +77,26 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_ /*! * \brief Pushes the \a reflectable to the specified array. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator); /*! * \brief Pushes the \a reflectable which has a custom type to the specified array. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator); /*! * \brief Pushes the specified \a reflectable which has custom type as a member to the specified object. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push( const Type &reflectable, const char *name, RAPIDJSON_NAMESPACE::Value::Object &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator); /*! * \brief Pushes the specified \a reflectable as a member to the specified object. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push( const Type &reflectable, const char *name, RAPIDJSON_NAMESPACE::Value::Object &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator); @@ -220,7 +220,7 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_ /*! * \brief Pushes the specified \a reflectable which has a custom type to the specified array. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { RAPIDJSON_NAMESPACE::Value objectValue(RAPIDJSON_NAMESPACE::kObjectType); @@ -232,7 +232,7 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAP /*! * \brief Pushes the specified \a reflectable to the specified array. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { RAPIDJSON_NAMESPACE::Value genericValue; @@ -243,7 +243,7 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAP /*! * \brief Pushes the specified \a reflectable which has custom type as a member to the specified object. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push( const Type &reflectable, const char *name, RAPIDJSON_NAMESPACE::Value::Object &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { @@ -256,7 +256,7 @@ void push( /*! * \brief Pushes the specified \a reflectable as a member to the specified object. */ -template , Type>>...> +template , Type>, AdaptedJsonSerializable>...> void push( const Type &reflectable, const char *name, RAPIDJSON_NAMESPACE::Value::Object &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { @@ -281,18 +281,9 @@ void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue, std::is_floating_point, std::is_pointer, + Traits::DisableIfAny, std::is_floating_point, std::is_pointer, Traits::IsSpecializationOf, Traits::All, Traits::Not>>>...> -void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors) -{ - if (!value.IsObject()) { - if (errors) { - errors->reportTypeMismatch(value.GetType()); - } - return; - } - pull(reflectable, value.GetObject(), errors); -} +void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors); /*! * \brief Pulls the integer/float/boolean from the specified value which is supposed and checked to contain the right type. @@ -401,7 +392,7 @@ namespace Detail { * \remarks Assumes that the array bounds have been checked before (to match the size of the tuple). */ template struct TuplePullHelper { - static void pull(Tuple &tuple, const RAPIDJSON_NAMESPACE::Value::Array &value, JsonDeserializationErrors *errors) + static void pull(Tuple &tuple, const RAPIDJSON_NAMESPACE::Value::ConstArray value, JsonDeserializationErrors *errors) { TuplePullHelper::pull(tuple, value, errors); JsonReflector::pull(std::get(tuple), value[N - 1], errors); @@ -409,7 +400,7 @@ template struct TuplePullHelper { }; template struct TuplePullHelper { - static void pull(Tuple &tuple, const RAPIDJSON_NAMESPACE::Value::Array &value, JsonDeserializationErrors *errors) + static void pull(Tuple &tuple, const RAPIDJSON_NAMESPACE::Value::ConstArray value, JsonDeserializationErrors *errors) { JsonReflector::pull(std::get<0>(tuple), value[0], errors); } @@ -420,7 +411,7 @@ template struct TuplePullHelper { * \brief Pulls the speciified \a reflectable which is tuple from the specified value which is checked to contain an array. */ template >...> -void pull(Type &reflectable, rapidjson::GenericValue> &value, JsonDeserializationErrors *errors) +void pull(Type &reflectable, const rapidjson::GenericValue> &value, JsonDeserializationErrors *errors) { if (!value.IsArray()) { if (errors) { @@ -459,7 +450,7 @@ inline void pull(Type &reflectable, const char *name, const rapidjson::GenericVa JsonDeserializationErrors *errors) { // find member - auto member = value.FindMember(name); + const auto member = value.FindMember(name); if (member == value.MemberEnd()) { return; // TODO: handle member missing } @@ -472,7 +463,7 @@ inline void pull(Type &reflectable, const char *name, const rapidjson::GenericVa } // actually pull value for member - pull(reflectable, value.FindMember(name)->value, errors); + pull(reflectable, member->value, errors); // restore previous error context if (errors) { @@ -480,6 +471,23 @@ inline void pull(Type &reflectable, const char *name, const rapidjson::GenericVa } } +/*! + * \brief Pulls the \a reflectable which has a custom type from the specified value which is supposed and checked to contain an object. + */ +template , std::is_floating_point, std::is_pointer, Traits::IsSpecializationOf, + Traits::All, Traits::Not>>>...> +void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors) +{ + if (!value.IsObject()) { + if (errors) { + errors->reportTypeMismatch(value.GetType()); + } + return; + } + pull(reflectable, value.GetObject(), errors); +} + // define functions providing high-level JSON serialization /*! @@ -584,9 +592,9 @@ Type fromJson(const char *json, std::size_t jsonSize, JsonDeserializationErrors /*! * \brief Deserializes the specified JSON from an std::string to \tparam Type. */ -template Type fromJson(const std::string &json) +template Type fromJson(const std::string &json, JsonDeserializationErrors *errors) { - return fromJson(json.data(), json.size()); + return fromJson(json.data(), json.size(), errors); } } // namespace JsonReflector