diff --git a/lib/jsonreflector.h b/lib/jsonreflector.h index a27b3ff..4e14a24 100644 --- a/lib/jsonreflector.h +++ b/lib/jsonreflector.h @@ -43,6 +43,16 @@ inline RAPIDJSON_NAMESPACE::StringBuffer documentToString(RAPIDJSON_NAMESPACE::D 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 { // define functions to "push" values to a RapidJSON array or object @@ -192,12 +202,18 @@ template , Traits::Not>>>...> void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue> &value) { + if (!value.IsObject()) { + return; // TODO: handle type mismatch + } pull(reflectable, value.GetObject()); } template , std::is_floating_point, std::is_pointer>...> inline void pull(Type &reflectable, RAPIDJSON_NAMESPACE::GenericValue>::ValueIterator &value) { + if (!value->Is()) { + return; // TODO: handle type mismatch + } reflectable = value->Get(); ++value; } @@ -205,18 +221,27 @@ inline void pull(Type &reflectable, RAPIDJSON_NAMESPACE::GenericValue, std::is_floating_point, std::is_pointer>...> inline void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue> &value) { + if (!value.Is()) { + return; // TODO: handle type mismatch + } reflectable = value.Get(); } template <> inline void pull(std::string &reflectable, RAPIDJSON_NAMESPACE::GenericValue>::ValueIterator &value) { + if (!value->IsString()) { + return; // TODO: handle type mismatch + } reflectable = value->GetString(); ++value; } template <> inline void pull(std::string &reflectable, const RAPIDJSON_NAMESPACE::GenericValue> &value) { + if (!value.IsString()) { + return; // TODO: handle type mismatch + } reflectable = value.GetString(); } @@ -228,6 +253,9 @@ template >>...> void pull(Type &reflectable, rapidjson::GenericValue>::ValueIterator &value) { + if (!value->IsArray()) { + return; // TODO: handle type mismatch + } pull(reflectable, value->GetArray()); ++value; } @@ -236,6 +264,9 @@ template , Traits::IsReservable, Traits::Not>>...> void pull(Type &reflectable, rapidjson::GenericValue>::ValueIterator &value) { + if (!value->IsArray()) { + return; // TODO: handle type mismatch + } auto array = value->GetArray(); reflectable.reserve(array.Size()); pull(reflectable, array); @@ -247,6 +278,9 @@ template >>...> void pull(Type &reflectable, const rapidjson::GenericValue> &value) { + if (!value.IsArray()) { + return; // TODO: handle type mismatch + } pull(reflectable, value.GetArray()); } @@ -254,6 +288,9 @@ template , Traits::IsReservable, Traits::Not>>...> void pull(Type &reflectable, const rapidjson::GenericValue> &value) { + if (!value.IsArray()) { + return; // TODO: handle type mismatch + } auto array = value.GetArray(); reflectable.reserve(array.Size()); pull(reflectable, array); @@ -274,6 +311,10 @@ void pull(Type &reflectable, rapidjson::GenericValue inline void pull(Type &reflectable, const char *name, const rapidjson::GenericValue>::ConstObject &value) { + auto member = value.FindMember(name); + if (member == value.MemberEnd()) { + return; // TODO: handle member missing + } pull(reflectable, value.FindMember(name)->value); } @@ -315,26 +356,35 @@ template >.. template , Type>>...> Type fromJson(const char *json, std::size_t jsonSize) { - RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); - document.Parse(json, jsonSize); + RAPIDJSON_NAMESPACE::Document doc(parseDocumentFromString(json, jsonSize)); + if (!doc.IsObject()) { + return Type(); + } + Type res; - pull(res, document.GetObject()); + pull(res, doc.GetObject()); return res; } template , std::is_floating_point>...> Type fromJson(const char *json, std::size_t jsonSize) { - RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); - document.Parse(json, jsonSize); - return document.Get(); + RAPIDJSON_NAMESPACE::Document doc(parseDocumentFromString(json, jsonSize)); + if (!doc.Is()) { + return Type(); + } + + return doc.Get(); } template >...> Type fromJson(const char *json, std::size_t jsonSize) { - RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kObjectType); - document.Parse(json, jsonSize); - return document.GetString(); + RAPIDJSON_NAMESPACE::Document doc(parseDocumentFromString(json, jsonSize)); + if (!doc.IsString()) { + return Type(); + } + + return doc.GetString(); } template Type fromJson(const std::string &json) diff --git a/lib/tests/jsonreflector.cpp b/lib/tests/jsonreflector.cpp index 6915bfa..855843f 100644 --- a/lib/tests/jsonreflector.cpp +++ b/lib/tests/jsonreflector.cpp @@ -112,6 +112,8 @@ class JSONReflectorTests : public TestFixture { CPPUNIT_TEST(testDeserializePrimitives); CPPUNIT_TEST(testDeserializeSimpleObjects); CPPUNIT_TEST(testDeserializeNestedObjects); + CPPUNIT_TEST(testHandlingParseError); + CPPUNIT_TEST(testHandlingTypeMismatch); CPPUNIT_TEST_SUITE_END(); public: @@ -125,6 +127,8 @@ public: void testDeserializePrimitives(); void testDeserializeSimpleObjects(); void testDeserializeNestedObjects(); + void testHandlingParseError(); + void testHandlingTypeMismatch(); private: }; @@ -315,3 +319,26 @@ void JSONReflectorTests::testDeserializeNestedObjects() 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\"}}"); +}