mirror of
https://github.com/oatpp/oatpp.git
synced 2025-02-17 17:29:57 +08:00
DTO: polymorphic fields.
This commit is contained in:
parent
fbf26c9c3c
commit
11e80b8a4a
@ -84,7 +84,8 @@ static bool Z__PROPERTY_INIT_##NAME(... /* default initializer for all cases */)
|
||||
} \
|
||||
\
|
||||
static TYPE Z__PROPERTY_INITIALIZER_PROXY_##NAME() { \
|
||||
static bool initialized = Z__PROPERTY_INIT_##NAME(1 /* init info if found */); \
|
||||
static bool initialized = Z__PROPERTY_INIT_##NAME(1 /* init info if found */, \
|
||||
1 /* init type selector if found */); \
|
||||
(void)initialized; \
|
||||
return TYPE(); \
|
||||
} \
|
||||
@ -114,7 +115,8 @@ static bool Z__PROPERTY_INIT_##NAME(... /* default initializer for all cases */)
|
||||
} \
|
||||
\
|
||||
static TYPE Z__PROPERTY_INITIALIZER_PROXY_##NAME() { \
|
||||
static bool initialized = Z__PROPERTY_INIT_##NAME(1 /* init info if found */); \
|
||||
static bool initialized = Z__PROPERTY_INIT_##NAME(1 /* init info if found */, \
|
||||
1 /* init type selector if found */); \
|
||||
(void)initialized; \
|
||||
return TYPE(); \
|
||||
} \
|
||||
@ -134,7 +136,7 @@ OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_SELECTOR(OATPP_MACRO_DTO_FIELD_, (__VA_ARGS
|
||||
|
||||
#define DTO_FIELD_INFO(NAME) \
|
||||
\
|
||||
static bool Z__PROPERTY_INIT_##NAME(int) { \
|
||||
static bool Z__PROPERTY_INIT_##NAME(int, ...) { \
|
||||
Z__PROPERTY_INIT_##NAME(); /* call first initialization */ \
|
||||
Z__PROPERTY_ADD_INFO_##NAME(&Z__PROPERTY_SINGLETON_##NAME()->info); \
|
||||
return true; \
|
||||
@ -142,6 +144,24 @@ static bool Z__PROPERTY_INIT_##NAME(int) { \
|
||||
\
|
||||
static void Z__PROPERTY_ADD_INFO_##NAME(oatpp::data::mapping::type::BaseObject::Property::Info* info)
|
||||
|
||||
|
||||
#define DTO_FIELD_TYPE_SELECTOR(NAME) \
|
||||
\
|
||||
class Z__PROPERTY_TYPE_SELECTOR_##NAME : public oatpp::BaseObject::Property::TypeSelector { \
|
||||
public: \
|
||||
const oatpp::Type* selectType(oatpp::BaseObject *self) override { \
|
||||
return Z__PROPERTY_TYPE_SELECTOR_METHOD_##NAME(static_cast<Z__CLASS*>(self)); \
|
||||
} \
|
||||
}; \
|
||||
\
|
||||
static bool Z__PROPERTY_INIT_##NAME(int, int) { \
|
||||
Z__PROPERTY_INIT_##NAME(1); /* call property info initialization */ \
|
||||
Z__PROPERTY_SINGLETON_##NAME()->info.typeSelector = new Z__PROPERTY_TYPE_SELECTOR_##NAME(); \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static oatpp::Type* Z__PROPERTY_TYPE_SELECTOR_METHOD_##NAME(Z__CLASS* self)
|
||||
|
||||
// FOR EACH
|
||||
|
||||
#define OATPP_MACRO_DTO_HC_EQ_PARAM_HC(INDEX, COUNT, X) \
|
||||
|
@ -36,6 +36,10 @@
|
||||
|
||||
#undef DTO_FIELD_INFO
|
||||
|
||||
// Type Selector
|
||||
|
||||
#undef DTO_FIELD_TYPE_SELECTOR
|
||||
|
||||
// Hashcode & Equals
|
||||
|
||||
#undef OATPP_MACRO_DTO_HC_EQ_PARAM_HC
|
||||
|
@ -39,6 +39,12 @@ namespace oatpp {
|
||||
*/
|
||||
typedef oatpp::data::mapping::type::ClassId ClassId;
|
||||
|
||||
/**
|
||||
* ObjectWrapper.
|
||||
*/
|
||||
template <class T, class Clazz = oatpp::data::mapping::type::__class::Void>
|
||||
using ObjectWrapper = oatpp::data::mapping::type::ObjectWrapper<T, Clazz>;
|
||||
|
||||
/**
|
||||
* ObjectWrapper over the `void*`.
|
||||
*/
|
||||
|
@ -280,7 +280,7 @@ public:
|
||||
*/
|
||||
template<class OtherInter>
|
||||
EnumObjectWrapper(const EnumObjectWrapper<T, OtherInter>& other)
|
||||
: type::ObjectWrapper<T, EnumObjectClass>(other.getPtr())
|
||||
: type::ObjectWrapper<T, EnumObjectClass>(other.m_ptr)
|
||||
{}
|
||||
|
||||
/**
|
||||
@ -290,7 +290,7 @@ public:
|
||||
*/
|
||||
template<class OtherInter>
|
||||
EnumObjectWrapper(EnumObjectWrapper<T, OtherInter>&& other)
|
||||
: type::ObjectWrapper<T, EnumObjectClass>(std::move(other.getPtr()))
|
||||
: type::ObjectWrapper<T, EnumObjectClass>(std::move(other.m_ptr))
|
||||
{}
|
||||
|
||||
inline EnumObjectWrapper& operator = (std::nullptr_t) {
|
||||
@ -306,7 +306,7 @@ public:
|
||||
|
||||
template<class OtherInter>
|
||||
inline EnumObjectWrapper& operator = (EnumObjectWrapper<T, OtherInter>&& other) {
|
||||
this->m_ptr = std::forward<std::shared_ptr<T>>(other.m_ptr);
|
||||
this->m_ptr = std::move(other.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -53,18 +53,50 @@ public:
|
||||
* Class to map object properties.
|
||||
*/
|
||||
class Property {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Property Type Selector.
|
||||
*/
|
||||
class TypeSelector {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Select property type.
|
||||
* @param self - pointer to `this` object.
|
||||
* @return - &id:oatpp::Type;.
|
||||
*/
|
||||
virtual const type::Type* selectType(BaseObject* self) = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Editional Info about Property.
|
||||
*/
|
||||
struct Info {
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*/
|
||||
std::string description = "";
|
||||
|
||||
/**
|
||||
* Pattern.
|
||||
*/
|
||||
std::string pattern = "";
|
||||
|
||||
/**
|
||||
* Required.
|
||||
*/
|
||||
bool required = false;
|
||||
|
||||
/**
|
||||
* Type selector.
|
||||
* &l:Property::TypeSelector;.
|
||||
*/
|
||||
TypeSelector* typeSelector = nullptr;
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
@ -271,6 +303,8 @@ namespace __class {
|
||||
*/
|
||||
template<class ObjT>
|
||||
class DTOWrapper : public ObjectWrapper<ObjT, __class::Object<ObjT>> {
|
||||
template<class Type>
|
||||
friend class DTOWrapper;
|
||||
public:
|
||||
typedef ObjT TemplateObjectType;
|
||||
typedef __class::Object<ObjT> TemplateObjectClass;
|
||||
@ -278,10 +312,32 @@ public:
|
||||
|
||||
OATPP_DEFINE_OBJECT_WRAPPER_DEFAULTS(DTOWrapper, TemplateObjectType, TemplateObjectClass)
|
||||
|
||||
template<class OtherT>
|
||||
DTOWrapper(const OtherT& other)
|
||||
: type::ObjectWrapper<ObjT, __class::Object<ObjT>>(other.m_ptr)
|
||||
{}
|
||||
|
||||
template<class OtherT>
|
||||
DTOWrapper(OtherT&& other)
|
||||
: type::ObjectWrapper<ObjT, __class::Object<ObjT>>(std::move(other.m_ptr))
|
||||
{}
|
||||
|
||||
static DTOWrapper createShared() {
|
||||
return std::make_shared<TemplateObjectType>();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
DTOWrapper& operator = (const DTOWrapper<T>& other) {
|
||||
this->m_ptr = other.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
DTOWrapper& operator = (DTOWrapper<T>&& other) {
|
||||
this->m_ptr = std::move(other.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename enabled = typename std::enable_if<std::is_same<T, std::nullptr_t>::value, void>::type
|
||||
>
|
||||
@ -312,6 +368,31 @@ public:
|
||||
return !operator == (other);
|
||||
}
|
||||
|
||||
static const std::unordered_map<std::string, BaseObject::Property*>& getPropertiesMap() {
|
||||
auto dispatcher = static_cast<const __class::AbstractObject::PolymorphicDispatcher*>(
|
||||
__class::Object<ObjT>::getType()->polymorphicDispatcher
|
||||
);
|
||||
return dispatcher->getProperties()->getMap();
|
||||
}
|
||||
|
||||
static const std::list<BaseObject::Property*>& getPropertiesList() {
|
||||
auto dispatcher = static_cast<const __class::AbstractObject::PolymorphicDispatcher*>(
|
||||
__class::Object<ObjT>::getType()->polymorphicDispatcher
|
||||
);
|
||||
return dispatcher->getProperties()->getList();
|
||||
}
|
||||
|
||||
static v_int64 getPropertiesCount() {
|
||||
auto dispatcher = static_cast<const __class::AbstractObject::PolymorphicDispatcher*>(
|
||||
__class::Object<ObjT>::getType()->polymorphicDispatcher
|
||||
);
|
||||
return dispatcher->getProperties()->getList().size();
|
||||
}
|
||||
|
||||
ObjectWrapper<void>& operator[](const std::string& propertyName) {
|
||||
return getPropertiesMap().at(propertyName)->getAsRef(this->m_ptr.get());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -119,6 +119,10 @@ class Void; // FWD
|
||||
template <class T, class Clazz = __class::Void>
|
||||
class ObjectWrapper {
|
||||
friend Void;
|
||||
template <class Q, class W>
|
||||
friend class ObjectWrapper;
|
||||
protected:
|
||||
static void checkType(const Type* _this, const Type* other);
|
||||
protected:
|
||||
std::shared_ptr<T> m_ptr;
|
||||
const Type* m_valueType;
|
||||
@ -174,12 +178,40 @@ public:
|
||||
, m_valueType(other.m_valueType)
|
||||
{}
|
||||
|
||||
template <class Q, class W>
|
||||
ObjectWrapper(const ObjectWrapper<Q, W>& other)
|
||||
: m_ptr(other.m_ptr)
|
||||
, m_valueType(other.m_valueType)
|
||||
{}
|
||||
|
||||
template <class Q, class W>
|
||||
ObjectWrapper(ObjectWrapper<Q, W>&& other)
|
||||
: m_ptr(std::move(other.m_ptr))
|
||||
, m_valueType(other.m_valueType)
|
||||
{}
|
||||
|
||||
inline ObjectWrapper& operator=(const ObjectWrapper& other){
|
||||
checkType(m_valueType, other.m_valueType);
|
||||
m_ptr = other.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ObjectWrapper& operator=(ObjectWrapper&& other){
|
||||
checkType(m_valueType, other.m_valueType);
|
||||
m_ptr = std::move(other.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Q, class W>
|
||||
inline ObjectWrapper& operator=(const ObjectWrapper<Q, W>& other){
|
||||
checkType(m_valueType, other.m_valueType);
|
||||
m_ptr = other.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Q, class W>
|
||||
inline ObjectWrapper& operator=(ObjectWrapper<Q, W>&& other){
|
||||
checkType(m_valueType, other.m_valueType);
|
||||
m_ptr = std::move(other.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
@ -197,7 +229,7 @@ public:
|
||||
return m_ptr.get();
|
||||
}
|
||||
|
||||
void setPtr(const std::shared_ptr<T>& ptr) {
|
||||
void resetPtr(const std::shared_ptr<T>& ptr = nullptr) {
|
||||
m_ptr = ptr;
|
||||
}
|
||||
|
||||
@ -261,6 +293,14 @@ public:
|
||||
: type::ObjectWrapper<void, __class::Void>(std::forward<std::shared_ptr<void>>(ptr))
|
||||
{}
|
||||
|
||||
Void(const Void& other)
|
||||
: type::ObjectWrapper<void, __class::Void>(other.getPtr(), other.getValueType())
|
||||
{}
|
||||
|
||||
Void(Void&& other)
|
||||
: type::ObjectWrapper<void, __class::Void>(std::move(other.getPtr()), other.getValueType())
|
||||
{}
|
||||
|
||||
template<typename T, typename C>
|
||||
Void(const ObjectWrapper<T, C>& other)
|
||||
: type::ObjectWrapper<void, __class::Void>(other.getPtr(), other.getValueType())
|
||||
@ -279,6 +319,18 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Void& operator = (const Void& other){
|
||||
m_ptr = other.m_ptr;
|
||||
m_valueType = other.getValueType();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Void& operator = (Void&& other){
|
||||
m_ptr = std::move(other.m_ptr);
|
||||
m_valueType = other.getValueType();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename C>
|
||||
inline Void& operator = (const ObjectWrapper<T, C>& other){
|
||||
m_ptr = other.m_ptr;
|
||||
@ -483,6 +535,15 @@ public:
|
||||
|
||||
};
|
||||
|
||||
template <class T, class Clazz>
|
||||
void ObjectWrapper<T, Clazz>::checkType(const Type* _this, const Type* other) {
|
||||
if(!_this->extends(other)) {
|
||||
throw std::runtime_error("[oatpp::data::mapping::type::ObjectWrapper::checkType()]: Error. "
|
||||
"Type mismatch: stored '" + std::string(_this->classId.name) + "' vs "
|
||||
"assigned '" + std::string(other->classId.name) + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
#define OATPP_DEFINE_OBJECT_WRAPPER_DEFAULTS(WRAPPER_NAME, OBJECT_TYPE, OBJECT_CLASS) \
|
||||
public: \
|
||||
WRAPPER_NAME(const std::shared_ptr<OBJECT_TYPE>& ptr, const type::Type* const valueType) \
|
||||
@ -521,7 +582,7 @@ public: \
|
||||
} \
|
||||
\
|
||||
inline WRAPPER_NAME& operator = (WRAPPER_NAME&& other) { \
|
||||
this->m_ptr = std::forward<std::shared_ptr<OBJECT_TYPE>>(other.m_ptr); \
|
||||
this->m_ptr = std::move(other.m_ptr); \
|
||||
return *this; \
|
||||
} \
|
||||
|
||||
|
@ -301,6 +301,7 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser::
|
||||
|
||||
caret.skipBlankChars();
|
||||
|
||||
std::vector<std::pair<oatpp::BaseObject::Property*, oatpp::String>> polymorphs;
|
||||
while (!caret.isAtChar('}') && caret.canContinue()) {
|
||||
|
||||
caret.skipBlankChars();
|
||||
@ -321,7 +322,14 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser::
|
||||
caret.skipBlankChars();
|
||||
|
||||
auto field = fieldIterator->second;
|
||||
field->set(static_cast<oatpp::BaseObject*>(object.get()), deserializer->deserialize(caret, field->type));
|
||||
|
||||
if(field->info.typeSelector) {
|
||||
auto label = caret.putLabel();
|
||||
skipValue(caret);
|
||||
polymorphs.emplace_back(field, label.toString()); // store polymorphs for later processing.
|
||||
} else {
|
||||
field->set(static_cast<oatpp::BaseObject *>(object.get()), deserializer->deserialize(caret, field->type));
|
||||
}
|
||||
|
||||
} else if (deserializer->getConfig()->allowUnknownFields) {
|
||||
caret.skipBlankChars();
|
||||
@ -348,6 +356,13 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser::
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for(auto& p : polymorphs) {
|
||||
parser::Caret polyCaret(p.second);
|
||||
auto selectedType = p.first->info.typeSelector->selectType(static_cast<oatpp::BaseObject *>(object.get()));
|
||||
auto value = deserializer->deserialize(polyCaret, selectedType);
|
||||
p.first->set(static_cast<oatpp::BaseObject *>(object.get()), oatpp::Void(value.getPtr(), p.first->type));
|
||||
}
|
||||
|
||||
return object;
|
||||
|
||||
} else {
|
||||
|
@ -158,7 +158,13 @@ void Serializer::serializeObject(Serializer* serializer,
|
||||
|
||||
for (auto const& field : fields) {
|
||||
|
||||
auto value = field->get(object);
|
||||
oatpp::Void value;
|
||||
if(field->info.typeSelector) {
|
||||
value = oatpp::Void(field->get(object).getPtr(), field->info.typeSelector->selectType(object));
|
||||
} else {
|
||||
value = field->get(object);
|
||||
}
|
||||
|
||||
if (value || config->includeNullFields || (field->info.required && config->alwaysIncludeRequired)) {
|
||||
(first) ? first = false : stream->writeSimple(",", 1);
|
||||
serializeString(stream, field->name, std::strlen(field->name), serializer->m_config->escapeFlags);
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
#include "ObjectTest.hpp"
|
||||
|
||||
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
|
||||
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/Types.hpp"
|
||||
|
||||
@ -94,6 +96,56 @@ class DtoD : public DtoA {
|
||||
|
||||
};
|
||||
|
||||
class DtoTypeA : public oatpp::DTO {
|
||||
|
||||
DTO_INIT(DtoTypeA, DTO)
|
||||
|
||||
DTO_FIELD(String, fieldA) = "type-A";
|
||||
|
||||
};
|
||||
|
||||
class DtoTypeB : public oatpp::DTO {
|
||||
|
||||
DTO_INIT(DtoTypeB, DTO)
|
||||
|
||||
DTO_FIELD(String, fieldB) = "type-B";
|
||||
|
||||
};
|
||||
|
||||
class PolymorphicDto1 : public oatpp::DTO {
|
||||
|
||||
DTO_INIT(PolymorphicDto1, DTO)
|
||||
|
||||
DTO_FIELD(String, type);
|
||||
DTO_FIELD(Object<DTO>, polymorph);
|
||||
|
||||
DTO_FIELD_TYPE_SELECTOR(polymorph) {
|
||||
if(self->type == "A") return Object<DtoTypeA>::Class::getType();
|
||||
if(self->type == "B") return Object<DtoTypeB>::Class::getType();
|
||||
return Object<DTO>::Class::getType();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PolymorphicDto2 : public oatpp::DTO {
|
||||
|
||||
DTO_INIT(PolymorphicDto2, DTO)
|
||||
|
||||
DTO_FIELD(String, type);
|
||||
|
||||
DTO_FIELD_INFO(polymorph) {
|
||||
info->description = "description";
|
||||
}
|
||||
DTO_FIELD(Object<DTO>, polymorph);
|
||||
|
||||
DTO_FIELD_TYPE_SELECTOR(polymorph) {
|
||||
if(self->type == "A") return Object<DtoTypeA>::Class::getType();
|
||||
if(self->type == "B") return Object<DtoTypeB>::Class::getType();
|
||||
return Object<DTO>::Class::getType();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#include OATPP_CODEGEN_END(DTO)
|
||||
|
||||
void runDtoInitializations() {
|
||||
@ -331,6 +383,114 @@ void ObjectTest::onRun() {
|
||||
OATPP_LOGI(TAG, "OK");
|
||||
}
|
||||
|
||||
{
|
||||
OATPP_LOGI(TAG, "Test 11...");
|
||||
|
||||
auto map = oatpp::Object<PolymorphicDto1>::getPropertiesMap();
|
||||
auto p = map["polymorph"];
|
||||
|
||||
OATPP_ASSERT(p->info.description == "");
|
||||
OATPP_ASSERT(p->info.typeSelector != nullptr);
|
||||
|
||||
OATPP_LOGI(TAG, "OK");
|
||||
}
|
||||
|
||||
{
|
||||
OATPP_LOGI(TAG, "Test 12...");
|
||||
|
||||
auto map = oatpp::Object<PolymorphicDto2>::getPropertiesMap();
|
||||
auto p = map["polymorph"];
|
||||
|
||||
OATPP_ASSERT(p->info.description == "description");
|
||||
OATPP_ASSERT(p->info.typeSelector != nullptr);
|
||||
|
||||
OATPP_LOGI(TAG, "OK");
|
||||
}
|
||||
|
||||
{
|
||||
OATPP_LOGI(TAG, "Test 13...");
|
||||
|
||||
auto dto = oatpp::Object<PolymorphicDto2>::createShared();
|
||||
OATPP_ASSERT(dto->type == nullptr)
|
||||
OATPP_ASSERT(dto->type.getValueType() == oatpp::String::Class::getType())
|
||||
OATPP_ASSERT(dto["type"] == nullptr)
|
||||
OATPP_ASSERT(dto["type"].getValueType() == oatpp::String::Class::getType())
|
||||
|
||||
dto["type"] = oatpp::String("hello");
|
||||
OATPP_ASSERT(dto->type == "hello");
|
||||
OATPP_ASSERT(dto->type.getValueType() == oatpp::String::Class::getType())
|
||||
|
||||
OATPP_LOGI(TAG, "OK");
|
||||
}
|
||||
|
||||
{
|
||||
OATPP_LOGI(TAG, "Test 14...");
|
||||
|
||||
auto dto = oatpp::Object<PolymorphicDto2>::createShared();
|
||||
bool thrown = false;
|
||||
|
||||
try{
|
||||
dto["type"] = oatpp::Int32(32);
|
||||
} catch(std::runtime_error e) {
|
||||
OATPP_LOGD(TAG, "error='%s'", e.what());
|
||||
thrown = true;
|
||||
}
|
||||
|
||||
OATPP_ASSERT(thrown)
|
||||
|
||||
OATPP_LOGI(TAG, "OK");
|
||||
}
|
||||
|
||||
{
|
||||
OATPP_LOGI(TAG, "Test 15...");
|
||||
|
||||
auto dto = oatpp::Object<PolymorphicDto2>::createShared();
|
||||
bool thrown = false;
|
||||
|
||||
try{
|
||||
dto["non-existing"];
|
||||
} catch(std::out_of_range e) {
|
||||
OATPP_LOGD(TAG, "error='%s'", e.what());
|
||||
thrown = true;
|
||||
}
|
||||
|
||||
OATPP_ASSERT(thrown)
|
||||
|
||||
OATPP_LOGI(TAG, "OK");
|
||||
}
|
||||
|
||||
{
|
||||
OATPP_LOGI(TAG, "Test 16...");
|
||||
|
||||
oatpp::parser::json::mapping::ObjectMapper mapper;
|
||||
|
||||
auto dto = PolymorphicDto1::createShared();
|
||||
|
||||
dto->type = "A";
|
||||
dto->polymorph = DtoTypeA::createShared();
|
||||
|
||||
OATPP_ASSERT(dto->polymorph.getValueType() == oatpp::Object<oatpp::DTO>::Class::getType())
|
||||
|
||||
auto json = mapper.writeToString(dto);
|
||||
OATPP_LOGD(TAG, "json0='%s'", json->c_str())
|
||||
|
||||
auto dtoClone = mapper.readFromString<oatpp::Object<PolymorphicDto1>>(json);
|
||||
|
||||
auto jsonClone = mapper.writeToString(dtoClone);
|
||||
OATPP_LOGD(TAG, "json1='%s'", jsonClone->c_str())
|
||||
|
||||
OATPP_ASSERT(json == jsonClone)
|
||||
|
||||
OATPP_ASSERT(dtoClone->polymorph)
|
||||
OATPP_ASSERT(dtoClone->polymorph.getValueType() == oatpp::Object<oatpp::DTO>::Class::getType())
|
||||
|
||||
auto polymorphClone = dtoClone->polymorph.staticCast<oatpp::Object<DtoTypeA>>();
|
||||
|
||||
OATPP_ASSERT(polymorphClone->fieldA == "type-A")
|
||||
|
||||
OATPP_LOGI(TAG, "OK");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}}}}}}
|
||||
|
Loading…
Reference in New Issue
Block a user