diff --git a/lib/json/errorhandling.h b/lib/json/errorhandling.h index 23ed0fc..46e1e92 100644 --- a/lib/json/errorhandling.h +++ b/lib/json/errorhandling.h @@ -23,6 +23,7 @@ namespace ReflectiveRapidJSON { */ enum class JsonDeserializationErrorKind : byte { TypeMismatch, + ArraySizeMismatch, }; /*! @@ -144,6 +145,7 @@ struct JsonDeserializationErrors : public std::vector JsonDeserializationErrors(); template void reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType); + void reportArraySizeMismatch(); /// \brief The name of the class or struct which is currently being processed. const char *currentRecord; @@ -152,7 +154,7 @@ struct JsonDeserializationErrors : public std::vector /// \brief The index in the array which is currently processed. std::size_t currentIndex; /// \brief The list of fatal error types in form of flags. - enum class ThrowOn : unsigned char { None = 0, TypeMismatch = 0x1 } throwOn; + enum class ThrowOn : unsigned char { None = 0, TypeMismatch = 0x1, ArraySizeMismatch = 0x2 } throwOn; }; /*! @@ -175,7 +177,7 @@ constexpr JsonDeserializationErrors::ThrowOn operator|(JsonDeserializationErrors } /*! - * \brief Reports a type missmatch between \tparam ExpectedType and \a presentType within the current context. + * \brief Reports a type mismatch between \tparam ExpectedType and \a presentType within the current context. */ template inline void JsonDeserializationErrors::reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType) { @@ -186,6 +188,19 @@ template inline void JsonDeserializationErrors::reportTy } } +/*! + * \brief Reports an array size mismatch. + * \todo Allow specifying expected and actual size. + * \remarks This can happen when mapping a JSON array to a C++ tuple. + */ +inline void JsonDeserializationErrors::reportArraySizeMismatch() +{ + emplace_back(JsonDeserializationErrorKind::ArraySizeMismatch, JsonType::Array, JsonType::Array, currentRecord, currentMember, currentIndex); + if (static_cast(throwOn) & static_cast(ThrowOn::ArraySizeMismatch)) { + throw back(); + } +} + } // namespace ReflectiveRapidJSON #endif // REFLECTIVE_RAPIDJSON_JSON_REFLECTOR_H diff --git a/lib/json/reflector.h b/lib/json/reflector.h index 038f11c..a0dda90 100644 --- a/lib/json/reflector.h +++ b/lib/json/reflector.h @@ -135,15 +135,19 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAP } namespace Detail { -template struct TupleHelper { + +/*! + * \brief The TuplePushHelper class helps serializing tuples to JSON arrays. + */ +template struct TuplePushHelper { static void push(const Tuple &tuple, RAPIDJSON_NAMESPACE::Value::Array &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { - TupleHelper::push(tuple, value, allocator); + TuplePushHelper::push(tuple, value, allocator); Reflector::push(std::get(tuple), value, allocator); } }; -template struct TupleHelper { +template struct TuplePushHelper { static void push(const Tuple &tuple, RAPIDJSON_NAMESPACE::Value::Array &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) { Reflector::push(std::get<0>(tuple), value, allocator); @@ -160,7 +164,7 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value::Array &value, RAP RAPIDJSON_NAMESPACE::Value arrayValue(RAPIDJSON_NAMESPACE::kArrayType); RAPIDJSON_NAMESPACE::Value::Array array(arrayValue.GetArray()); array.Reserve(std::tuple_size::value, allocator); - Detail::TupleHelper::value>::push(reflectable, array, allocator); + Detail::TuplePushHelper::value>::push(reflectable, array, allocator); value.PushBack(array, allocator); } @@ -273,7 +277,7 @@ void push( RAPIDJSON_NAMESPACE::Value arrayValue(RAPIDJSON_NAMESPACE::kArrayType); RAPIDJSON_NAMESPACE::Value::Array array(arrayValue.GetArray()); array.Reserve(std::tuple_size::value, allocator); - Detail::TupleHelper::value>::push(reflectable, array, allocator); + Detail::TuplePushHelper::value>::push(reflectable, array, allocator); value.AddMember(RAPIDJSON_NAMESPACE::StringRef(name), array, allocator); } @@ -503,6 +507,75 @@ void pull(Type &reflectable, rapidjson::GenericValue struct TuplePullHelper { + static void pull(Tuple &tuple, const RAPIDJSON_NAMESPACE::Value::Array &value, JsonDeserializationErrors *errors) + { + TuplePullHelper::pull(tuple, value, errors); + Reflector::pull(std::get(tuple), value[N - 1], errors); + } +}; + +template struct TuplePullHelper { + static void pull(Tuple &tuple, const RAPIDJSON_NAMESPACE::Value::Array &value, JsonDeserializationErrors *errors) + { + Reflector::pull(std::get<0>(tuple), value[0], errors); + } +}; +} // namespace Detail + +/*! + * \brief Pulls the speciified \a reflectable which is tuple from the specified value iterator which is checked to contain an array. + */ +template >...> +void pull(Type &reflectable, rapidjson::GenericValue>::ValueIterator &value, JsonDeserializationErrors *errors) +{ + if (!value->IsArray()) { + if (errors) { + errors->reportTypeMismatch(value->GetType()); + } + return; + } + auto array = value->GetArray(); + if (array.Size() != std::tuple_size::value) { + if (errors) { + // FIXME: report expected and actual size + errors->reportArraySizeMismatch(); + } + return; + } + Detail::TuplePullHelper::value>::pull(reflectable, array, errors); + ++value; +} + +/*! + * \brief Pulls the speciified \a reflectable which is tuple from the specified value iterator which is checked to contain an array. + */ +template >...> +void pull(Type &reflectable, rapidjson::GenericValue> &value, JsonDeserializationErrors *errors) +{ + if (!value.IsArray()) { + if (errors) { + errors->reportTypeMismatch(value.GetType()); + } + return; + } + auto array = value.GetArray(); + if (array.Size() != std::tuple_size::value) { + if (errors) { + // FIXME: report expected and actual size + errors->reportArraySizeMismatch(); + } + return; + } + Detail::TuplePullHelper::value>::pull(reflectable, array, errors); +} + /*! * \brief Pulls the speciified member of \a reflectable which has a custom type from the specified object. * \remarks It is checked whether the object actually contains the member. If not, the missing member is ignored. So currently all members diff --git a/lib/tests/jsonreflector.cpp b/lib/tests/jsonreflector.cpp index c72952e..3440776 100644 --- a/lib/tests/jsonreflector.cpp +++ b/lib/tests/jsonreflector.cpp @@ -286,6 +286,7 @@ void JsonReflectorTests::testDeserializePrimitives() Reflector::pull(double1, array, &errors); Reflector::pull(bool2, array, &errors); + CPPUNIT_ASSERT_EQUAL(0_st, errors.size()); CPPUNIT_ASSERT_EQUAL("a"s, str1); CPPUNIT_ASSERT_EQUAL(5, int1); CPPUNIT_ASSERT_EQUAL(5e6f, float1); @@ -293,6 +294,22 @@ void JsonReflectorTests::testDeserializePrimitives() CPPUNIT_ASSERT_EQUAL(true, bool1); CPPUNIT_ASSERT_EQUAL(4.125, double1); CPPUNIT_ASSERT_EQUAL(false, bool2); + + // deserialize primitives as tuple + tuple arrayAsTuple; + Reflector::pull(arrayAsTuple, doc, &errors); + CPPUNIT_ASSERT_EQUAL(0_st, errors.size()); + CPPUNIT_ASSERT_EQUAL("a"s, get<0>(arrayAsTuple)); + CPPUNIT_ASSERT_EQUAL(5, get<1>(arrayAsTuple)); + CPPUNIT_ASSERT_EQUAL(5e6f, get<2>(arrayAsTuple)); + CPPUNIT_ASSERT_EQUAL("test"s, get<3>(arrayAsTuple)); + CPPUNIT_ASSERT_EQUAL(true, get<4>(arrayAsTuple)); + CPPUNIT_ASSERT_EQUAL(4.125, get<5>(arrayAsTuple)); + CPPUNIT_ASSERT_EQUAL(false, get<6>(arrayAsTuple)); + tuple anotherTuple; + Reflector::pull(anotherTuple, doc, &errors); + CPPUNIT_ASSERT_EQUAL(1_st, errors.size()); + CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::ArraySizeMismatch, errors.front().kind); } /*!