Fix incomplete use of AdaptedJsonSerializable and handling tuple

This commit is contained in:
Martchus 2017-11-12 00:44:47 +01:00
parent 4655387c4d
commit e9324f0ec3
3 changed files with 73 additions and 33 deletions

View File

@ -220,21 +220,41 @@ void JsonGeneratorTests::testCustomSerialization()
*/
void JsonGeneratorTests::test3rdPartyAdaption()
{
// test whether specializations of AdaptedJsonSerializable are generated
static_assert(
ReflectiveRapidJSON::AdaptedJsonSerializable<NotJsonSerializable>::value, "can serialize NotJsonSerializable because of adaption macro");
static_assert(ReflectiveRapidJSON::AdaptedJsonSerializable<NestedNotJsonSerializable>::value,
"can serialize NestedNotJsonSerializable because of adaption macro");
static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable<OtherNotJsonSerializable>::value,
"can not serialize OtherNotJsonSerializable because adaption macro missing");
static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable<ReallyNotJsonSerializable>::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<NotJsonSerializable>(str));
CPPUNIT_ASSERT_EQUAL(test.butSerializableAnyways, parsedTest.butSerializableAnyways);
JsonDeserializationErrors errors;
const auto parsedSimple(ReflectiveRapidJSON::JsonReflector::fromJson<NotJsonSerializable>(strSimple, &errors));
CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
CPPUNIT_ASSERT_EQUAL(simple.butSerializableAnyways, parsedSimple.butSerializableAnyways);
const auto parsedNested(ReflectiveRapidJSON::JsonReflector::fromJson<NestedNotJsonSerializable>(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

View File

@ -88,7 +88,7 @@ struct StructWithCustomTypes : public JsonSerializable<StructWithCustomTypes> {
};
/*!
* \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<NotJsonSerializable> asArrayElement;
tuple<int, NotJsonSerializable> 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

View File

@ -77,26 +77,26 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_
/*!
* \brief Pushes the \a reflectable to the specified array.
*/
template <typename Type, Traits::DisableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::DisableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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 <typename Type, Traits::EnableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::EnableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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 <typename Type, Traits::EnableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::EnableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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 <typename Type, Traits::DisableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::DisableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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 <typename Type, Traits::EnableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::EnableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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 <typename Type, Traits::DisableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::DisableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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 <typename Type, Traits::EnableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::EnableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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 <typename Type, Traits::DisableIf<std::is_base_of<JsonSerializable<Type>, Type>>...>
template <typename Type, Traits::DisableIfAny<std::is_base_of<JsonSerializable<Type>, Type>, AdaptedJsonSerializable<Type>>...>
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<RAPIDJSON_N
* \brief Pulls the \a reflectable which has a custom type from the specified value which is supposed and checked to contain an object.
*/
template <typename Type,
Traits::DisableIfAny<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>,
Traits::DisableIfAny<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>, Traits::IsSpecializationOf<Type, std::tuple>,
Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>>...>
void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
if (!value.IsObject()) {
if (errors) {
errors->reportTypeMismatch<Type>(value.GetType());
}
return;
}
pull(reflectable, value.GetObject(), errors);
}
void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &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 <class Tuple, std::size_t N> 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<Tuple, N - 1>::pull(tuple, value, errors);
JsonReflector::pull(std::get<N - 1>(tuple), value[N - 1], errors);
@ -409,7 +400,7 @@ template <class Tuple, std::size_t N> struct TuplePullHelper {
};
template <class Tuple> struct TuplePullHelper<Tuple, 1> {
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 <class Tuple> struct TuplePullHelper<Tuple, 1> {
* \brief Pulls the speciified \a reflectable which is tuple from the specified value which is checked to contain an array.
*/
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::tuple>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &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<Type>(reflectable, value.FindMember(name)->value, errors);
pull<Type>(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 <typename Type,
Traits::DisableIfAny<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>, Traits::IsSpecializationOf<Type, std::tuple>,
Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>>...>
void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
if (!value.IsObject()) {
if (errors) {
errors->reportTypeMismatch<Type>(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 <typename Type> Type fromJson(const std::string &json)
template <typename Type> Type fromJson(const std::string &json, JsonDeserializationErrors *errors)
{
return fromJson<Type>(json.data(), json.size());
return fromJson<Type>(json.data(), json.size(), errors);
}
} // namespace JsonReflector