diff --git a/src/oatpp/core/data/mapping/type/Type.cpp b/src/oatpp/core/data/mapping/type/Type.cpp index 354b6abc..c021afae 100644 --- a/src/oatpp/core/data/mapping/type/Type.cpp +++ b/src/oatpp/core/data/mapping/type/Type.cpp @@ -98,12 +98,14 @@ Type::Type(const ClassId& pClassId, const char* pNameQualifier, Creator pCreator, PropertiesGetter pPropertiesGetter, - void* pPolymorphicDispatcher) + void* pPolymorphicDispatcher, + InterpretationMap&& pInterpretationMap) : classId(pClassId) , nameQualifier(pNameQualifier) , creator(pCreator) , propertiesGetter(pPropertiesGetter) , polymorphicDispatcher(pPolymorphicDispatcher) + , interpretationMap(pInterpretationMap) {} }}}} diff --git a/src/oatpp/core/data/mapping/type/Type.hpp b/src/oatpp/core/data/mapping/type/Type.hpp index 1f5abfff..ea51786c 100644 --- a/src/oatpp/core/data/mapping/type/Type.hpp +++ b/src/oatpp/core/data/mapping/type/Type.hpp @@ -332,6 +332,39 @@ public: Void& getAsRef(void* object); }; +public: + + class AbstractInterpretation { + public: + virtual Void toInterpretation(const Void& originalValue) const = 0; + virtual Void fromInterpretation(const Void& interValue) const = 0; + virtual Type* getInterpretationType() const = 0; + }; + + template + class Interpretation : public AbstractInterpretation { + public: + + Void toInterpretation(const Void& originalValue) const override { + return interpret(originalValue.staticCast()); + } + + Void fromInterpretation(const Void& interValue) const override { + return reproduce(interValue.staticCast()); + } + + Type* getInterpretationType() const override { + return InterWrapper::Class::getType(); + } + + public: + + virtual InterWrapper interpret(const OriginalWrapper& value) const = 0; + virtual OriginalWrapper reproduce(const InterWrapper& value) const = 0; + + }; + + typedef std::unordered_map InterpretationMap; public: typedef Void (*Creator)(); @@ -345,12 +378,14 @@ public: * @param pCreator - function pointer of Creator - function to create instance of this type. * @param pPropertiesGetter - function to get properties of the type. * @param pPolymorphicDispatcher - dispatcher to correctly address methods of the type. + * @param pInterpretationMap - Map of type Interpretations. */ Type(const ClassId& pClassId, const char* pNameQualifier, Creator pCreator = nullptr, PropertiesGetter pPropertiesGetter = nullptr, - void* pPolymorphicDispatcher = nullptr); + void* pPolymorphicDispatcher = nullptr, + InterpretationMap&& pInterpretationMap = {}); /** * type class id. @@ -381,6 +416,11 @@ public: * PolymorphicDispatcher - is an object to forward polymorphic calls to a correct object of type `Type`. */ const void* const polymorphicDispatcher; + + /** + * Map of type Interpretations. + */ + const InterpretationMap interpretationMap; }; diff --git a/src/oatpp/parser/json/mapping/Deserializer.cpp b/src/oatpp/parser/json/mapping/Deserializer.cpp index e008ead0..e44bc723 100644 --- a/src/oatpp/parser/json/mapping/Deserializer.cpp +++ b/src/oatpp/parser/json/mapping/Deserializer.cpp @@ -358,12 +358,29 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser:: } +const oatpp::Type::AbstractInterpretation* Deserializer::findTypeInterpretation(const oatpp::Type* type) { + const auto& intMap = type->interpretationMap; + for(auto& name : m_config->enableInterpretations) { + auto it = intMap.find(name); + if(it != intMap.end()) { + return it->second; + } + } + return nullptr; +} + oatpp::Void Deserializer::deserialize(parser::Caret& caret, const Type* const type) { auto id = type->classId.id; auto& method = m_methods[id]; if(method) { return (*method)(this, caret, type); } else { + + auto* interpretation = findTypeInterpretation(type); + if(interpretation) { + return interpretation->fromInterpretation(deserialize(caret, interpretation->getInterpretationType())); + } + throw std::runtime_error("[oatpp::parser::json::mapping::Deserializer::deserialize()]: " "Error. No deserialize method for type '" + std::string(type->classId.name) + "'"); } diff --git a/src/oatpp/parser/json/mapping/Deserializer.hpp b/src/oatpp/parser/json/mapping/Deserializer.hpp index 5bc2a382..29dc48c2 100644 --- a/src/oatpp/parser/json/mapping/Deserializer.hpp +++ b/src/oatpp/parser/json/mapping/Deserializer.hpp @@ -110,6 +110,11 @@ public: */ bool allowUnknownFields = true; + /** + * Enable type interpretations. + */ + std::vector enableInterpretations = {}; + }; public: @@ -279,6 +284,8 @@ private: static oatpp::Void deserializeEnum(Deserializer* deserializer, parser::Caret& caret, const Type* const type); static oatpp::Void deserializeObject(Deserializer* deserializer, parser::Caret& caret, const Type* const type); +private: + const oatpp::Type::AbstractInterpretation* findTypeInterpretation(const oatpp::Type* type); private: std::shared_ptr m_config; std::vector m_methods; diff --git a/src/oatpp/parser/json/mapping/Serializer.cpp b/src/oatpp/parser/json/mapping/Serializer.cpp index cd7489d5..f2026fa5 100644 --- a/src/oatpp/parser/json/mapping/Serializer.cpp +++ b/src/oatpp/parser/json/mapping/Serializer.cpp @@ -171,6 +171,17 @@ void Serializer::serializeObject(Serializer* serializer, } +const oatpp::Type::AbstractInterpretation* Serializer::findTypeInterpretation(const oatpp::Type* type) { + const auto& intMap = type->interpretationMap; + for(auto& name : m_config->enableInterpretations) { + auto it = intMap.find(name); + if(it != intMap.end()) { + return it->second; + } + } + return nullptr; +} + void Serializer::serialize(data::stream::ConsistentOutputStream* stream, const oatpp::Void& polymorph) { @@ -179,8 +190,16 @@ void Serializer::serialize(data::stream::ConsistentOutputStream* stream, if(method) { (*method)(this, stream, polymorph); } else { - throw std::runtime_error("[oatpp::parser::json::mapping::Serializer::serialize()]: " - "Error. No serialize method for type '" + std::string(polymorph.valueType->classId.name) + "'"); + + auto* interpretation = findTypeInterpretation(polymorph.valueType); + if(interpretation) { + serialize(stream, interpretation->toInterpretation(polymorph)); + } else { + throw std::runtime_error("[oatpp::parser::json::mapping::Serializer::serialize()]: " + "Error. No serialize method for type '" + + std::string(polymorph.valueType->classId.name) + "'"); + } + } } diff --git a/src/oatpp/parser/json/mapping/Serializer.hpp b/src/oatpp/parser/json/mapping/Serializer.hpp index 02c9c84a..9ad72a12 100644 --- a/src/oatpp/parser/json/mapping/Serializer.hpp +++ b/src/oatpp/parser/json/mapping/Serializer.hpp @@ -91,6 +91,11 @@ public: */ oatpp::String beautifierNewLine = "\n"; + /** + * Enable type interpretations. + */ + std::vector enableInterpretations = {}; + }; public: typedef void (*SerializerMethod)(Serializer*, @@ -182,6 +187,8 @@ private: void serialize(data::stream::ConsistentOutputStream* stream, const oatpp::Void& polymorph); +private: + const oatpp::Type::AbstractInterpretation* findTypeInterpretation(const oatpp::Type* type); private: std::shared_ptr m_config; std::vector m_methods; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d4d46ed3..81e962aa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,6 +19,8 @@ add_executable(oatppAllTests oatpp/core/data/mapping/type/AnyTest.hpp oatpp/core/data/mapping/type/EnumTest.cpp oatpp/core/data/mapping/type/EnumTest.hpp + oatpp/core/data/mapping/type/InterpretationTest.cpp + oatpp/core/data/mapping/type/InterpretationTest.hpp oatpp/core/data/mapping/type/ListTest.cpp oatpp/core/data/mapping/type/ListTest.hpp oatpp/core/data/mapping/type/ObjectTest.cpp diff --git a/test/oatpp/AllTestsMain.cpp b/test/oatpp/AllTestsMain.cpp index 0a65c6a7..a7487c11 100644 --- a/test/oatpp/AllTestsMain.cpp +++ b/test/oatpp/AllTestsMain.cpp @@ -40,6 +40,8 @@ #include "oatpp/core/data/mapping/type/TypeTest.hpp" #include "oatpp/core/data/mapping/type/AnyTest.hpp" #include "oatpp/core/data/mapping/type/EnumTest.hpp" +#include "oatpp/core/data/mapping/type/InterpretationTest.hpp" + #include "oatpp/core/data/stream/BufferStreamTest.hpp" #include "oatpp/core/data/stream/ChunkedBufferTest.hpp" #include "oatpp/core/data/share/LazyStringMapTest.hpp" @@ -99,9 +101,10 @@ void runTests() { OATPP_RUN_TEST(oatpp::test::core::data::mapping::type::UnorderedMapTest); OATPP_RUN_TEST(oatpp::test::core::data::mapping::type::AnyTest); OATPP_RUN_TEST(oatpp::test::core::data::mapping::type::EnumTest); - OATPP_RUN_TEST(oatpp::test::core::data::mapping::type::ObjectTest); + OATPP_RUN_TEST(oatpp::test::core::data::mapping::type::InterpretationTest); + OATPP_RUN_TEST(oatpp::test::async::LockTest); OATPP_RUN_TEST(oatpp::test::parser::CaretTest); diff --git a/test/oatpp/core/data/mapping/type/InterpretationTest.cpp b/test/oatpp/core/data/mapping/type/InterpretationTest.cpp new file mode 100644 index 00000000..920fdb56 --- /dev/null +++ b/test/oatpp/core/data/mapping/type/InterpretationTest.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#include "InterpretationTest.hpp" + +#include "oatpp/parser/json/mapping/ObjectMapper.hpp" +#include "oatpp/core/Types.hpp" +#include "oatpp/core/macro/codegen.hpp" + +namespace oatpp { namespace test { namespace core { namespace data { namespace mapping { namespace type { + +namespace { + +#include OATPP_CODEGEN_BEGIN(DTO) + +struct VPoint { + v_int32 x; + v_int32 y; + v_int32 z; +}; + +struct VLine { + VPoint p1; + VPoint p2; +}; + +namespace __class { + class PointClass; + class LineClass; +} + +typedef oatpp::data::mapping::type::Primitive Point; +typedef oatpp::data::mapping::type::Primitive Line; + +namespace __class { + + class PointClass { + private: + + class PointDto : public oatpp::DTO { + + DTO_INIT(PointDto, DTO) + + DTO_FIELD(Int32, x); + DTO_FIELD(Int32, y); + DTO_FIELD(Int32, z); + + }; + + class Inter : public oatpp::Type::Interpretation> { + public: + + oatpp::Object interpret(const Point& value) const override { + auto dto = PointDto::createShared(); + dto->x = value->x; + dto->y = value->y; + dto->z = value->z; + return dto; + } + + Point reproduce(const oatpp::Object& value) const override { + return Point({value->x, value->y, value->z}); + } + + }; + + public: + + static const oatpp::ClassId CLASS_ID; + + static oatpp::Type* getType(){ + static Type type( + CLASS_ID, nullptr, nullptr, nullptr, nullptr, + { + {"test", new Inter()} + } + ); + return &type; + } + + }; + + const oatpp::ClassId PointClass::CLASS_ID("test::Point"); + + class LineClass { + private: + + class LineDto : public oatpp::DTO { + + DTO_INIT(LineDto, DTO) + + DTO_FIELD(Point, p1); + DTO_FIELD(Point, p2); + + }; + + class Inter : public oatpp::Type::Interpretation> { + public: + + oatpp::Object interpret(const Line& value) const override { + auto dto = LineDto::createShared(); + dto->p1 = {value->p1.x, value->p1.y, value->p1.z}; + dto->p2 = {value->p2.x, value->p2.y, value->p2.z}; + return dto; + } + + Line reproduce(const oatpp::Object& value) const override { + return Line({{value->p1->x, value->p1->y, value->p1->z}, + {value->p2->x, value->p2->y, value->p2->z}}); + } + + }; + + public: + + static const oatpp::ClassId CLASS_ID; + + static oatpp::Type* getType(){ + static Type type( + CLASS_ID, nullptr, nullptr, nullptr, nullptr, + { + {"test", new Inter()} + } + ); + return &type; + } + + }; + + const oatpp::ClassId LineClass::CLASS_ID("test::Line"); + + +} + +#include OATPP_CODEGEN_END(DTO) + +} + +void InterpretationTest::onRun() { + + oatpp::parser::json::mapping::ObjectMapper mapper; + + { + auto config = mapper.getSerializer()->getConfig(); + config->enableInterpretations = {"test"}; + config->useBeautifier = false; + } + + { + auto config = mapper.getDeserializer()->getConfig(); + config->enableInterpretations = {"test"}; + } + + Point p1 ({1, 2, 3}); + Point p2 ({11, 12, 13}); + + Line l ({p1, p2}); + + auto json1 = mapper.writeToString(l); + + OATPP_LOGD(TAG, "json1='%s'", json1->c_str()); + + auto rl = mapper.readFromString(json1); + + auto json2 = mapper.writeToString(rl); + + OATPP_LOGD(TAG, "json2='%s'", json2->c_str()); + + OATPP_ASSERT(json1 == json2); + +} + +}}}}}} \ No newline at end of file diff --git a/test/oatpp/core/data/mapping/type/InterpretationTest.hpp b/test/oatpp/core/data/mapping/type/InterpretationTest.hpp new file mode 100644 index 00000000..e3c9b823 --- /dev/null +++ b/test/oatpp/core/data/mapping/type/InterpretationTest.hpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#ifndef oatpp_test_core_data_mapping_type_InterpretationTest_hpp +#define oatpp_test_core_data_mapping_type_InterpretationTest_hpp + +#include "oatpp-test/UnitTest.hpp" + +namespace oatpp { namespace test { namespace core { namespace data { namespace mapping { namespace type { + +class InterpretationTest : public UnitTest{ +public: + + InterpretationTest():UnitTest("TEST[core::data::mapping::type::InterpretationTest]"){} + void onRun() override; + +}; + +}}}}}} + +#endif // oatpp_test_core_data_mapping_type_InterpretationTest_hpp