Improve error handling

* Throw ParseResult when parsing error occurs
* Silence type mismatch or missing members for now
This commit is contained in:
Martchus 2017-10-27 20:20:22 +02:00
parent 97f1dc57cf
commit 1b1d07ef8c
2 changed files with 86 additions and 9 deletions

View File

@ -43,6 +43,16 @@ inline RAPIDJSON_NAMESPACE::StringBuffer documentToString(RAPIDJSON_NAMESPACE::D
return buffer; return buffer;
} }
inline RAPIDJSON_NAMESPACE::Document parseDocumentFromString(const char *json, std::size_t jsonSize)
{
RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType);
const RAPIDJSON_NAMESPACE::ParseResult parseRes = document.Parse(json, jsonSize);
if (parseRes.IsError()) {
throw parseRes;
}
return document;
}
namespace Reflector { namespace Reflector {
// define functions to "push" values to a RapidJSON array or object // define functions to "push" values to a RapidJSON array or object
@ -192,12 +202,18 @@ template <typename Type,
Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>>...> 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) void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value)
{ {
if (!value.IsObject()) {
return; // TODO: handle type mismatch
}
pull<Type>(reflectable, value.GetObject()); pull<Type>(reflectable, value.GetObject());
} }
template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>>...> template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>>...>
inline void pull(Type &reflectable, RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value) inline void pull(Type &reflectable, RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value)
{ {
if (!value->Is<Type>()) {
return; // TODO: handle type mismatch
}
reflectable = value->Get<Type>(); reflectable = value->Get<Type>();
++value; ++value;
} }
@ -205,18 +221,27 @@ inline void pull(Type &reflectable, RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_
template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>>...> template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>>...>
inline void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value) inline void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value)
{ {
if (!value.Is<Type>()) {
return; // TODO: handle type mismatch
}
reflectable = value.Get<Type>(); reflectable = value.Get<Type>();
} }
template <> template <>
inline void pull<std::string>(std::string &reflectable, RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value) inline void pull<std::string>(std::string &reflectable, RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value)
{ {
if (!value->IsString()) {
return; // TODO: handle type mismatch
}
reflectable = value->GetString(); reflectable = value->GetString();
++value; ++value;
} }
template <> inline void pull<std::string>(std::string &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value) template <> inline void pull<std::string>(std::string &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value)
{ {
if (!value.IsString()) {
return; // TODO: handle type mismatch
}
reflectable = value.GetString(); reflectable = value.GetString();
} }
@ -228,6 +253,9 @@ template <typename Type,
Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...> Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value) void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value)
{ {
if (!value->IsArray()) {
return; // TODO: handle type mismatch
}
pull(reflectable, value->GetArray()); pull(reflectable, value->GetArray());
++value; ++value;
} }
@ -236,6 +264,9 @@ template <typename Type,
Traits::EnableIf<Traits::IsIteratable<Type>, Traits::IsReservable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...> Traits::EnableIf<Traits::IsIteratable<Type>, Traits::IsReservable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value) void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ValueIterator &value)
{ {
if (!value->IsArray()) {
return; // TODO: handle type mismatch
}
auto array = value->GetArray(); auto array = value->GetArray();
reflectable.reserve(array.Size()); reflectable.reserve(array.Size());
pull(reflectable, array); pull(reflectable, array);
@ -247,6 +278,9 @@ template <typename Type,
Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...> Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value) void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value)
{ {
if (!value.IsArray()) {
return; // TODO: handle type mismatch
}
pull(reflectable, value.GetArray()); pull(reflectable, value.GetArray());
} }
@ -254,6 +288,9 @@ template <typename Type,
Traits::EnableIf<Traits::IsIteratable<Type>, Traits::IsReservable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...> Traits::EnableIf<Traits::IsIteratable<Type>, Traits::IsReservable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>...>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value) void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value)
{ {
if (!value.IsArray()) {
return; // TODO: handle type mismatch
}
auto array = value.GetArray(); auto array = value.GetArray();
reflectable.reserve(array.Size()); reflectable.reserve(array.Size());
pull(reflectable, array); pull(reflectable, array);
@ -274,6 +311,10 @@ void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<c
template <typename Type> template <typename Type>
inline void pull(Type &reflectable, const char *name, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value) inline void pull(Type &reflectable, const char *name, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value)
{ {
auto member = value.FindMember(name);
if (member == value.MemberEnd()) {
return; // TODO: handle member missing
}
pull<Type>(reflectable, value.FindMember(name)->value); pull<Type>(reflectable, value.FindMember(name)->value);
} }
@ -315,26 +356,35 @@ template <typename Type, Traits::EnableIfAny<std::is_same<Type, const char *>>..
template <typename Type, Traits::EnableIfAny<std::is_base_of<JSONSerializable<Type>, Type>>...> Type fromJson(const char *json, std::size_t jsonSize) template <typename Type, Traits::EnableIfAny<std::is_base_of<JSONSerializable<Type>, Type>>...> Type fromJson(const char *json, std::size_t jsonSize)
{ {
RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); RAPIDJSON_NAMESPACE::Document doc(parseDocumentFromString(json, jsonSize));
document.Parse(json, jsonSize); if (!doc.IsObject()) {
return Type();
}
Type res; Type res;
pull<Type>(res, document.GetObject()); pull<Type>(res, doc.GetObject());
return res; return res;
} }
template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>>...> template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>>...>
Type fromJson(const char *json, std::size_t jsonSize) Type fromJson(const char *json, std::size_t jsonSize)
{ {
RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); RAPIDJSON_NAMESPACE::Document doc(parseDocumentFromString(json, jsonSize));
document.Parse(json, jsonSize); if (!doc.Is<Type>()) {
return document.Get<Type>(); return Type();
}
return doc.Get<Type>();
} }
template <typename Type, Traits::EnableIfAny<std::is_same<Type, std::string>>...> Type fromJson(const char *json, std::size_t jsonSize) template <typename Type, Traits::EnableIfAny<std::is_same<Type, std::string>>...> Type fromJson(const char *json, std::size_t jsonSize)
{ {
RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); RAPIDJSON_NAMESPACE::Document doc(parseDocumentFromString(json, jsonSize));
document.Parse(json, jsonSize); if (!doc.IsString()) {
return document.GetString(); return Type();
}
return doc.GetString();
} }
template <typename Type> Type fromJson(const std::string &json) template <typename Type> Type fromJson(const std::string &json)

View File

@ -112,6 +112,8 @@ class JSONReflectorTests : public TestFixture {
CPPUNIT_TEST(testDeserializePrimitives); CPPUNIT_TEST(testDeserializePrimitives);
CPPUNIT_TEST(testDeserializeSimpleObjects); CPPUNIT_TEST(testDeserializeSimpleObjects);
CPPUNIT_TEST(testDeserializeNestedObjects); CPPUNIT_TEST(testDeserializeNestedObjects);
CPPUNIT_TEST(testHandlingParseError);
CPPUNIT_TEST(testHandlingTypeMismatch);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
public: public:
@ -125,6 +127,8 @@ public:
void testDeserializePrimitives(); void testDeserializePrimitives();
void testDeserializeSimpleObjects(); void testDeserializeSimpleObjects();
void testDeserializeNestedObjects(); void testDeserializeNestedObjects();
void testHandlingParseError();
void testHandlingTypeMismatch();
private: private:
}; };
@ -315,3 +319,26 @@ void JSONReflectorTests::testDeserializeNestedObjects()
CPPUNIT_ASSERT_EQUAL(false, testObj.boolean); CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
} }
} }
void JSONReflectorTests::testHandlingParseError()
{
try {
NestingObject::fromJson("{\"name\":nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
"\"test\",\"boolean\":false}}");
CPPUNIT_FAIL("expected ParseResult thrown");
} catch (const RAPIDJSON_NAMESPACE::ParseResult &res) {
CPPUNIT_ASSERT_EQUAL(RAPIDJSON_NAMESPACE::kParseErrorValueInvalid, res.Code());
CPPUNIT_ASSERT_EQUAL(9_st, res.Offset());
}
}
void JSONReflectorTests::testHandlingTypeMismatch()
{
NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":\"42\",\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
"\"test\",\"boolean\":false}}");
NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":1,\"text\":"
"\"test\",\"boolean\":false}}");
NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":\"this is not an object\"}");
NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":\"42\",\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
"\"test\",\"boolean\":\"false\"}}");
}