Merge pull request #937 from oatpp/object_remapper

Object remapper
This commit is contained in:
Leonid Stryzhevskyi 2024-05-09 18:48:11 -07:00 committed by GitHub
commit 02c9fbf8e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 1244 additions and 105 deletions

View File

@ -20,7 +20,9 @@
**News**
- ⚠️ Attention! Oat++ main repo is bumping it's version to 1.4.0. While 1.4.0 is **IN DEVELOPMENT** use `1.3.0-latest` tag.
- ⚠️ Attention! Oat++ main repo is bumping its version to 1.4.0. While 1.4.0 is **IN DEVELOPMENT** use `1.3.0-latest` tag.
- Follow the [changelog](https://github.com/oatpp/oatpp/blob/master/changelog/1.4.0.md) for news and features in version `1.4.0`.
- Consider supporting Oat++ via the [GitHub sponsors](https://github.com/sponsors/oatpp) page.
---

View File

@ -8,6 +8,8 @@ Contents:
- [URL Encoder And Decoder](#url-encoder-and-decoder)
- [Introduce async::ConditionVariable](#async-condition-variable)
- [oatpp::Tree](#oatpptree)
- [Remapper](#remapper)
- [Restructuring](#restructuring)
@ -49,6 +51,61 @@ OATPP_ASSERT(decoded == data)
...
```
## oatpp::Tree
New mapping-enabled type `oatpp::Tree` for flexible data access.
```cpp
ENDPOINT("POST", "users", createUser,
BODY_DTO(oatpp::Tree, user))
{
oatpp::String name = user["name"];
v_uint16 age = user["age"];
auto& subs = user["subscriptions"].getVector();
for(auto& s : subs) {
...
}
...
}
```
Any node of oatpp::Tree can be mapped to DTO or to any mapping-enabled oatpp type - see [Remapper](#remapper)
## Remapper
`oatpp::data::mapping::Remapper`.
Remapper can be user to remap any oatpp type to any oatpp type.
UnorderedFields can be mapped to DTOs, DTOs to vectors of values, vector items to other DTOs, DTOs to Trees, etc...
```cpp
class User : public oatpp::DTO {
DTO_INIT(User, DTO)
DTO_FIELD(String, name);
DTO_FIELD(UInt32, age);
};
...
oatpp::data::mapping::Remapper remapper;
auto user = User::createShared();
user->name = "Jane";
user->age = "25";
auto tree = remapper.remap<oatpp::Tree>(user); // remap to tree
auto fields = remapper.remap<oatpp::Fields<oatpp::Tree>>(user); // remap to Fields
auto otherDto = remapper.remap<oatpp::Object<OtherDto>>(user); // remap to OtherDto
auto values = remapper.remap<oatpp::Vector<oatpp::Tree>>(user); // remap to Vector
oatpp::String name = values[0];
v_uint32 age = values[1];
```
## Restructuring
### Files

View File

@ -68,6 +68,8 @@ add_library(oatpp
oatpp/data/buffer/IOBuffer.hpp
oatpp/data/buffer/Processor.cpp
oatpp/data/buffer/Processor.hpp
oatpp/data/mapping/ObjectRemapper.cpp
oatpp/data/mapping/ObjectRemapper.hpp
oatpp/data/mapping/ObjectMapper.cpp
oatpp/data/mapping/ObjectMapper.hpp
oatpp/data/mapping/ObjectToTreeMapper.cpp

View File

@ -73,8 +73,9 @@ static v_int64 Z__PROPERTY_OFFSET_##NAME() { \
static oatpp::data::type::BaseObject::Property* Z__PROPERTY_SINGLETON_##NAME() { \
static oatpp::data::type::BaseObject::Property* property = \
new oatpp::data::type::BaseObject::Property(Z__PROPERTY_OFFSET_##NAME(), \
#NAME, \
TYPE::Class::getType()); \
#NAME, \
#NAME, \
TYPE::Class::getType()); \
return property; \
} \
\
@ -104,8 +105,9 @@ static v_int64 Z__PROPERTY_OFFSET_##NAME() { \
static oatpp::data::type::BaseObject::Property* Z__PROPERTY_SINGLETON_##NAME() { \
static oatpp::data::type::BaseObject::Property* property = \
new oatpp::data::type::BaseObject::Property(Z__PROPERTY_OFFSET_##NAME(), \
QUALIFIER, \
TYPE::Class::getType()); \
QUALIFIER, \
#NAME, \
TYPE::Class::getType()); \
return property; \
} \
\

View File

@ -52,24 +52,27 @@ OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_SELECTOR(MACRO, (__VA_ARGS__)) (NAME, __VA_
#define OATPP_MACRO_DTO_ENUM_VALUE_1(NAME, VAL) \
{ \
oatpp::data::type::EnumValueInfo<EnumType> entry = {EnumType::NAME, index ++, #NAME, nullptr}; \
oatpp::data::type::EnumValueInfo<EnumType> entry = {EnumType::NAME, index ++, #NAME, #NAME, nullptr}; \
info.byName.insert({#NAME, entry}); \
info.byUnqualifiedName.insert({#NAME, entry}); \
info.byValue.insert({static_cast<v_uint64>(EnumType::NAME), entry}); \
info.byIndex.push_back(entry); \
}
#define OATPP_MACRO_DTO_ENUM_VALUE_2(NAME, VAL, QUALIFIER) \
{ \
oatpp::data::type::EnumValueInfo<EnumType> entry = {EnumType::NAME, index ++, QUALIFIER, nullptr}; \
oatpp::data::type::EnumValueInfo<EnumType> entry = {EnumType::NAME, index ++, QUALIFIER, #NAME, nullptr}; \
info.byName.insert({QUALIFIER, entry}); \
info.byUnqualifiedName.insert({#NAME, entry}); \
info.byValue.insert({static_cast<v_uint64>(EnumType::NAME), entry}); \
info.byIndex.push_back(entry); \
}
#define OATPP_MACRO_DTO_ENUM_VALUE_3(NAME, VAL, QUALIFIER, DESCRIPTION) \
{ \
oatpp::data::type::EnumValueInfo<EnumType> entry = {EnumType::NAME, index ++, QUALIFIER, DESCRIPTION}; \
oatpp::data::type::EnumValueInfo<EnumType> entry = {EnumType::NAME, index ++, QUALIFIER, #NAME, DESCRIPTION}; \
info.byName.insert({QUALIFIER, entry}); \
info.byUnqualifiedName.insert({#NAME, entry}); \
info.byValue.insert({static_cast<v_uint64>(EnumType::NAME), entry}); \
info.byIndex.push_back(entry); \
}

View File

@ -0,0 +1,107 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* 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 "ObjectRemapper.hpp"
namespace oatpp { namespace data { namespace mapping {
oatpp::Void ObjectRemapper::remap(const Tree& tree, const oatpp::Type* toType, ErrorStack& errorStack) const {
TreeToObjectMapper::State state;
state.tree = std::addressof(tree);
state.config = &m_treeToObjectConfig;
const auto & result = m_treeToObjectMapper.map(state, toType);
if(!state.errorStack.empty()) {
errorStack = std::move(state.errorStack);
return nullptr;
}
return result;
}
oatpp::Void ObjectRemapper::remap(const oatpp::Void& polymorph, const oatpp::Type* toType, ErrorStack& errorStack) const {
if(polymorph == nullptr) {
return nullptr;
}
/* if polymorph is a Tree - we can map it right away */
if(polymorph.getValueType() == oatpp::Tree::Class::getType()) {
auto tree = static_cast<const Tree*>(polymorph.get());
return remap(*tree, toType, errorStack);
}
Tree tree;
{
ObjectToTreeMapper::State state;
state.tree = &tree;
state.config = &m_objectToTreeConfig;
m_objectToTreeMapper.map(state, polymorph);
if(!state.errorStack.empty()) {
errorStack = std::move(state.errorStack);
return nullptr;
}
}
/* if expected type is a Tree (root element is a Tree) - then we can just move mapped tree */
if(toType == data::type::Tree::Class::getType()) {
return oatpp::Tree(std::move(tree));
}
return remap(tree, toType, errorStack);
}
ObjectToTreeMapper::Config& ObjectRemapper::objectToTreeConfig() {
return m_objectToTreeConfig;
}
TreeToObjectMapper::Config& ObjectRemapper::treeToObjectConfig() {
return m_treeToObjectConfig;
}
ObjectToTreeMapper& ObjectRemapper::objectToTreeMapper() {
return m_objectToTreeMapper;
}
TreeToObjectMapper& ObjectRemapper::treeToObjectMapper() {
return m_treeToObjectMapper;
}
const ObjectToTreeMapper::Config& ObjectRemapper::objectToTreeConfig() const {
return m_objectToTreeConfig;
}
const TreeToObjectMapper::Config& ObjectRemapper::treeToObjectConfig() const {
return m_treeToObjectConfig;
}
const ObjectToTreeMapper& ObjectRemapper::objectToTreeMapper() const {
return m_objectToTreeMapper;
}
const TreeToObjectMapper& ObjectRemapper::treeToObjectMapper() const {
return m_treeToObjectMapper;
}
}}}

View File

@ -0,0 +1,100 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* 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_data_mapping_ObjectRemapper_hpp
#define oatpp_data_mapping_ObjectRemapper_hpp
#include "./ObjectToTreeMapper.hpp"
#include "./TreeToObjectMapper.hpp"
#include "oatpp/Types.hpp"
namespace oatpp { namespace data { namespace mapping {
class ObjectRemapper {
protected:
ObjectToTreeMapper::Config m_objectToTreeConfig;
TreeToObjectMapper::Config m_treeToObjectConfig;
ObjectToTreeMapper m_objectToTreeMapper;
TreeToObjectMapper m_treeToObjectMapper;
public:
ObjectRemapper() = default;
virtual ~ObjectRemapper() = default;
oatpp::Void remap(const Tree& tree, const oatpp::Type* toType, ErrorStack& errorStack) const;
template<class Wrapper>
Wrapper remap(const Tree& tree, ErrorStack& errorStack) const {
auto toType = Wrapper::Class::getType();
return remap(tree, toType, errorStack).template cast<Wrapper>();
}
template<class Wrapper>
Wrapper remap(const Tree& tree) const {
auto toType = Wrapper::Class::getType();
ErrorStack errorStack;
const auto& result = remap(tree, toType, errorStack).template cast<Wrapper>();
if(!errorStack.empty()) {
throw MappingError(std::move(errorStack));
}
return result;
}
oatpp::Void remap(const oatpp::Void& polymorph, const oatpp::Type* toType, ErrorStack& errorStack) const;
template<class Wrapper>
Wrapper remap(const oatpp::Void& polymorph, ErrorStack& errorStack) const {
auto toType = Wrapper::Class::getType();
return remap(polymorph, toType, errorStack).template cast<Wrapper>();
}
template<class Wrapper>
Wrapper remap(const oatpp::Void& polymorph) const {
auto toType = Wrapper::Class::getType();
ErrorStack errorStack;
const auto& result = remap(polymorph, toType, errorStack).template cast<Wrapper>();
if(!errorStack.empty()) {
throw MappingError(std::move(errorStack));
}
return result;
}
ObjectToTreeMapper::Config& objectToTreeConfig();
TreeToObjectMapper::Config& treeToObjectConfig();
ObjectToTreeMapper& objectToTreeMapper();
TreeToObjectMapper& treeToObjectMapper();
const ObjectToTreeMapper::Config& objectToTreeConfig() const;
const TreeToObjectMapper::Config& treeToObjectConfig() const;
const ObjectToTreeMapper& objectToTreeMapper() const;
const TreeToObjectMapper& treeToObjectMapper() const;
};
}}}
#endif // oatpp_data_mapping_ObjectRemapper_hpp

View File

@ -127,7 +127,7 @@ void ObjectToTreeMapper::mapEnum(const ObjectToTreeMapper* mapper, State& state,
);
data::type::EnumInterpreterError e = data::type::EnumInterpreterError::OK;
mapper->map(state, polymorphicDispatcher->toInterpretation(polymorph, e));
mapper->map(state, polymorphicDispatcher->toInterpretation(polymorph, state.config->useUnqualifiedEnumNames, e));
if(e == data::type::EnumInterpreterError::OK) {
return;
@ -273,23 +273,28 @@ void ObjectToTreeMapper::mapObject(const ObjectToTreeMapper* mapper, State& stat
}
if(field->info.required && value == nullptr) {
oatpp::String key;
state.config->useUnqualifiedFieldNames ? key = field->unqualifiedName : key = field->name;
state.errorStack.push("[oatpp::data::mapping::ObjectToTreeMapper::mapObject()]: "
"Error. " + std::string(type->nameQualifier) + "::"
+ std::string(field->name) + " is required!");
+ key + " is required!");
return;
}
if (value || state.config->includeNullFields || (field->info.required && state.config->alwaysIncludeRequired)) {
oatpp::String key;
state.config->useUnqualifiedFieldNames ? key = field->unqualifiedName : key = field->name;
State nestedState;
nestedState.tree = childrenOperator.putPair(oatpp::String(field->name), {});
nestedState.tree = childrenOperator.putPair(key, {});
nestedState.config = state.config;
mapper->map(nestedState, value);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(nestedState.errorStack);
state.errorStack.push("[oatpp::data::mapping::ObjectToTreeMapper::mapObject()]: field='" + oatpp::String(field->name) + "'");
state.errorStack.push("[oatpp::data::mapping::ObjectToTreeMapper::mapObject()]: field='" + key + "'");
return;
}

View File

@ -37,6 +37,8 @@ public:
bool includeNullFields = true;
bool alwaysIncludeRequired = false;
bool alwaysIncludeNullCollectionElements = false;
bool useUnqualifiedFieldNames = false;
bool useUnqualifiedEnumNames = false;
std::vector<std::string> enabledInterpretations = {};
};

View File

@ -36,8 +36,10 @@ Tree::Attributes::Attributes()
{}
Tree::Attributes::Attributes(const Attributes& other)
: m_attributes(other.m_attributes != nullptr ? new std::unordered_map<type::String, type::String>(*other.m_attributes) : nullptr)
{}
: Attributes()
{
operator=(other);
}
Tree::Attributes::Attributes(Attributes&& other) noexcept
: m_attributes(other.m_attributes)
@ -46,37 +48,101 @@ Tree::Attributes::Attributes(Attributes&& other) noexcept
}
Tree::Attributes& Tree::Attributes::operator = (const Attributes& other) {
if(other.m_attributes) {
if(m_attributes) {
*m_attributes = *other.m_attributes;
} else {
m_attributes = new std::unordered_map<type::String, type::String>(*other.m_attributes);
m_attributes = new Attrs(*other.m_attributes);
}
for(auto & po : m_attributes->order){
po.second = &m_attributes->map.at(po.first.lock());
}
} else {
delete m_attributes;
m_attributes = nullptr;
}
return *this;
}
Tree::Attributes& Tree::Attributes::operator = (Attributes&& other) noexcept {
delete m_attributes;
m_attributes = other.m_attributes;
other.m_attributes = nullptr;
return *this;
}
Tree::Attributes::~Attributes() {
delete m_attributes;
m_attributes = nullptr;
}
void Tree::Attributes::initAttributes() {
if(m_attributes == nullptr) {
m_attributes = new Attrs();
}
}
type::String& Tree::Attributes::operator [] (const type::String& key) {
initAttributes();
auto it = m_attributes->map.find(key);
if(it == m_attributes->map.end()) {
auto& result = m_attributes->map[key];
m_attributes->order.emplace_back(key.getPtr(), &result);
return result;
}
return it->second;
}
const type::String& Tree::Attributes::operator [] (const type::String& key) const {
if(m_attributes != nullptr) {
auto it = m_attributes->map.find(key);
if (it != m_attributes->map.end()) {
return it->second;
}
}
throw std::runtime_error("[oatpp::data::mapping::Tree::Attributes::operator []]: const operator[] can't add items.");
}
std::pair<type::String, std::reference_wrapper<type::String>> Tree::Attributes::operator [] (v_uint64 index) {
if(m_attributes != nullptr) {
auto &item = m_attributes->order.at(index);
return {item.first.lock(), *item.second};
}
throw std::runtime_error("[oatpp::data::mapping::Tree::Attributes::operator []]: const operator[] can't get item - empty attributes.");
}
std::pair<type::String, std::reference_wrapper<const type::String>> Tree::Attributes::operator [] (v_uint64 index) const {
if(m_attributes != nullptr) {
auto &item = m_attributes->order.at(index);
return {item.first.lock(), *item.second};
}
throw std::runtime_error("[oatpp::data::mapping::Tree::Attributes::operator []]: const operator[] can't get item - empty attributes.");
}
type::String Tree::Attributes::get(const type::String& key) const {
if(m_attributes == nullptr) return nullptr;
auto it = m_attributes->map.find(key);
if(it != m_attributes->map.end()) {
return it->second;
}
return nullptr;
}
bool Tree::Attributes::empty() const {
return m_attributes == nullptr || m_attributes->empty();
return m_attributes == nullptr || m_attributes->map.empty();
}
v_uint64 Tree::Attributes::size() const {
if(m_attributes) {
return m_attributes->size();
return m_attributes->map.size();
}
return 0;
}
@ -725,13 +791,9 @@ TreeMap::TreeMap(TreeMap&& other) noexcept {
TreeMap& TreeMap::operator = (const TreeMap& other) {
m_map = other.m_map;
m_order.resize(other.m_order.size());
for(v_uint64 i = 0; i < other.m_order.size(); i ++) {
auto& po = other.m_order[i];
auto& pt = m_order[i];
pt.first = po.first;
pt.second = &m_map.at(po.first.lock());
m_order = other.m_order;
for(auto & po : m_order){
po.second = &m_map.at(po.first.lock());
}
return *this;
}

View File

@ -72,7 +72,14 @@ public:
class Attributes {
private:
std::unordered_map<type::String, type::String>* m_attributes;
struct Attrs {
std::unordered_map<type::String, type::String> map;
std::vector<std::pair<std::weak_ptr<std::string>, type::String*>> order;
};
private:
void initAttributes();
private:
Attrs* m_attributes;
public:
Attributes();
@ -84,8 +91,15 @@ public:
~Attributes();
bool empty() const;
type::String& operator [] (const type::String& key);
const type::String& operator [] (const type::String& key) const;
std::pair<type::String, std::reference_wrapper<type::String>> operator [] (v_uint64 index);
std::pair<type::String, std::reference_wrapper<const type::String>> operator [] (v_uint64 index) const;
type::String get(const type::String& key) const;
bool empty() const;
v_uint64 size() const;
};

View File

@ -177,7 +177,7 @@ oatpp::Void TreeToObjectMapper::mapEnum(const TreeToObjectMapper* mapper, State&
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapEnum()]");
return nullptr;
}
const auto& result = polymorphicDispatcher->fromInterpretation(value, e);
const auto& result = polymorphicDispatcher->fromInterpretation(value, state.config->useUnqualifiedEnumNames, e);
if(e == data::type::EnumInterpreterError::OK) {
return result;
@ -287,7 +287,13 @@ oatpp::Void TreeToObjectMapper::mapObject(const TreeToObjectMapper* mapper, Stat
auto dispatcher = static_cast<const oatpp::data::type::__class::AbstractObject::PolymorphicDispatcher*>(type->polymorphicDispatcher);
auto object = dispatcher->createObject();
const auto& fieldsMap = dispatcher->getProperties()->getMap();
const std::unordered_map<std::string, BaseObject::Property*>* fieldsMap;
if(state.config->useUnqualifiedFieldNames) {
fieldsMap = std::addressof(dispatcher->getProperties()->getUnqualifiedMap());
} else {
fieldsMap = std::addressof(dispatcher->getProperties()->getMap());
}
std::vector<std::pair<oatpp::BaseObject::Property*, const Tree*>> polymorphs;
@ -298,8 +304,8 @@ oatpp::Void TreeToObjectMapper::mapObject(const TreeToObjectMapper* mapper, Stat
const auto& pair = childrenOperator.getPair(i);
auto fieldIterator = fieldsMap.find(pair.first);
if(fieldIterator != fieldsMap.end()){
auto fieldIterator = fieldsMap->find(pair.first);
if(fieldIterator != fieldsMap->end()){
auto field = fieldIterator->second;

View File

@ -35,6 +35,8 @@ public:
struct Config {
bool allowUnknownFields = true;
bool useUnqualifiedFieldNames = false;
bool useUnqualifiedEnumNames = false;
std::vector<std::string> enabledInterpretations = {};
};

View File

@ -93,10 +93,10 @@ namespace __class {
virtual type::Void createObject() const = 0;
virtual type::Void toInterpretation(const type::Void& enumValue, EnumInterpreterError& error) const = 0;
virtual type::Void fromInterpretation(const type::Void& interValue, EnumInterpreterError& error) const = 0;
virtual type::Void toInterpretation(const type::Void& enumValue, bool useUnqualifiedNames, EnumInterpreterError& error) const = 0;
virtual type::Void fromInterpretation(const type::Void& interValue, bool useUnqualifiedNames, EnumInterpreterError& error) const = 0;
virtual type::Type* getInterpretationType() const = 0;
virtual std::vector<type::Any> getInterpretedEnum() const = 0;
virtual std::vector<type::Any> getInterpretedEnum(bool useUnqualifiedNames) const = 0;
};
@ -128,6 +128,12 @@ struct EnumValueInfo {
* &id:oatpp::data::share::StringKeyLabel;.
*/
const data::share::StringKeyLabel name;
/**
* Name of the enum entry <br>
* &id:oatpp::data::share::StringKeyLabel;.
*/
const data::share::StringKeyLabel unqualifiedName;
/**
* Description of the enum etry. <br>
@ -141,6 +147,7 @@ struct EnumInfo {
public:
const char* nameQualifier = nullptr;
std::unordered_map<data::share::StringKeyLabel, EnumValueInfo<T>> byName;
std::unordered_map<data::share::StringKeyLabel, EnumValueInfo<T>> byUnqualifiedName;
std::unordered_map<v_uint64, EnumValueInfo<T>> byValue;
std::vector<EnumValueInfo<T>> byIndex;
};
@ -181,8 +188,8 @@ public:
public:
constexpr static bool notNull = notnull;
public:
static Void toInterpretation(const Void& enumValue, EnumInterpreterError& error);
static Void fromInterpretation(const Void& interValue, EnumInterpreterError& error);
static Void toInterpretation(const Void& enumValue, bool useUnqualifiedNames, EnumInterpreterError& error);
static Void fromInterpretation(const Void& interValue, bool useUnqualifiedNames, EnumInterpreterError& error);
static Type* getInterpretationType();
};
@ -203,8 +210,8 @@ public:
public:
constexpr static bool notNull = notnull;
public:
static Void toInterpretation(const Void& enumValue, EnumInterpreterError& error);
static Void fromInterpretation(const Void& interValue, EnumInterpreterError& error);
static Void toInterpretation(const Void& enumValue, bool useUnqualifiedNames, EnumInterpreterError& error);
static Void fromInterpretation(const Void& interValue, bool useUnqualifiedNames, EnumInterpreterError& error);
static Type* getInterpretationType();
};
@ -398,6 +405,20 @@ public:
throw std::runtime_error("[oatpp::data::type::Enum::getEntryByName()]: Error. Entry not found.");
}
/**
* Get &l:EnumValueInfo <T>; by unqualified name.
* @param name - name of the enum entry.
* @return - &l:EnumValueInfo <T>;.
* @throws - `std::runtime_error` if not found.
*/
static const EnumValueInfo<T>& getEntryByUnqualifiedName(const String& unqualifiedName) {
auto it = EnumMeta<T>::getInfo()->byUnqualifiedName.find(unqualifiedName);
if(it != EnumMeta<T>::getInfo()->byUnqualifiedName.end()) {
return it->second;
}
throw std::runtime_error("[oatpp::data::type::Enum::getEntryByUnqualifiedName()]: Error. Entry not found.");
}
/**
* Get &l:EnumValueInfo <T>; by enum value.
* @param value - enum value.
@ -456,7 +477,7 @@ template <class T>
using Enum = EnumObjectWrapper<T, EnumInterpreterAsString<T, false>>;
template<class T, bool notnull>
Void EnumInterpreterAsString<T, notnull>::toInterpretation(const Void& enumValue, EnumInterpreterError& error) {
Void EnumInterpreterAsString<T, notnull>::toInterpretation(const Void& enumValue, bool useUnqualifiedNames, EnumInterpreterError& error) {
typedef EnumObjectWrapper<T, EnumInterpreterAsString<T, notnull>> EnumOW;
if(enumValue.getValueType() != EnumOW::Class::getType()) {
@ -474,11 +495,14 @@ Void EnumInterpreterAsString<T, notnull>::toInterpretation(const Void& enumValue
const auto& ow = enumValue.template cast<EnumOW>();
const auto& entry = EnumOW::getEntryByValue(*ow);
if(useUnqualifiedNames) {
return entry.unqualifiedName.toString();
}
return entry.name.toString();
}
template<class T, bool notnull>
Void EnumInterpreterAsString<T, notnull>::fromInterpretation(const Void& interValue, EnumInterpreterError& error) {
Void EnumInterpreterAsString<T, notnull>::fromInterpretation(const Void& interValue, bool useUnqualifiedNames, EnumInterpreterError& error) {
typedef EnumObjectWrapper<T, EnumInterpreterAsString<T, notnull>> EnumOW;
if(interValue.getValueType() != String::Class::getType()) {
@ -495,6 +519,9 @@ Void EnumInterpreterAsString<T, notnull>::fromInterpretation(const Void& interVa
}
try {
if(useUnqualifiedNames) {
return EnumOW(EnumOW::getEntryByUnqualifiedName(interValue.template cast<String>()).value);
}
return EnumOW(EnumOW::getEntryByName(interValue.template cast<String>()).value);
} catch (const std::runtime_error&) { // TODO - add a specific error for this.
error = EnumInterpreterError::ENTRY_NOT_FOUND;
@ -508,8 +535,8 @@ Type* EnumInterpreterAsString<T, notnull>::getInterpretationType() {
}
template<class T, bool notnull>
Void EnumInterpreterAsNumber<T, notnull>::toInterpretation(const Void& enumValue, EnumInterpreterError& error) {
Void EnumInterpreterAsNumber<T, notnull>::toInterpretation(const Void& enumValue, bool useUnqualifiedNames, EnumInterpreterError& error) {
(void) useUnqualifiedNames;
typedef EnumObjectWrapper<T, EnumInterpreterAsNumber<T, notnull>> EnumOW;
typedef typename std::underlying_type<T>::type EnumUT;
typedef typename ObjectWrapperByUnderlyingType<EnumUT>::ObjectWrapper UTOW;
@ -533,7 +560,8 @@ Void EnumInterpreterAsNumber<T, notnull>::toInterpretation(const Void& enumValue
}
template<class T, bool notnull>
Void EnumInterpreterAsNumber<T, notnull>::fromInterpretation(const Void& interValue, EnumInterpreterError& error) {
Void EnumInterpreterAsNumber<T, notnull>::fromInterpretation(const Void& interValue, bool useUnqualifiedNames, EnumInterpreterError& error) {
(void) useUnqualifiedNames;
typedef EnumObjectWrapper<T, EnumInterpreterAsNumber<T, notnull>> EnumOW;
typedef typename std::underlying_type<T>::type EnumUT;
@ -586,19 +614,19 @@ namespace __class {
return type::Void(std::make_shared<T>(), getType());
}
type::Void toInterpretation(const type::Void& enumValue, EnumInterpreterError& error) const override {
return Interpreter::toInterpretation(enumValue, error);
type::Void toInterpretation(const type::Void& enumValue, bool useUnqualifiedNames, EnumInterpreterError& error) const override {
return Interpreter::toInterpretation(enumValue, useUnqualifiedNames, error);
}
type::Void fromInterpretation(const type::Void& interValue, EnumInterpreterError& error) const override {
return Interpreter::fromInterpretation(interValue, error);
type::Void fromInterpretation(const type::Void& interValue, bool useUnqualifiedNames, EnumInterpreterError& error) const override {
return Interpreter::fromInterpretation(interValue, useUnqualifiedNames, error);
}
type::Type* getInterpretationType() const override {
return Interpreter::getInterpretationType();
}
std::vector<type::Any> getInterpretedEnum() const override {
std::vector<type::Any> getInterpretedEnum(bool useUnqualifiedNames) const override {
typedef type::EnumObjectWrapper<T, Interpreter> EnumOW;
@ -606,7 +634,7 @@ namespace __class {
for(const auto& e : EnumOW::getEntries()) {
type::EnumInterpreterError error = type::EnumInterpreterError::OK;
result.push_back(type::Any(toInterpretation(EnumOW(e.value), error)));
result.push_back(type::Any(toInterpretation(EnumOW(e.value), useUnqualifiedNames, error)));
if(error != type::EnumInterpreterError::OK) {
throw std::runtime_error("[oatpp::data::type::__class::Enum<T, Interpreter>::getInterpretedEnum()]: Unknown error.");
}

View File

@ -58,33 +58,48 @@ void* BaseObject::getBasePointer() const {
BaseObject::Property* BaseObject::Properties::pushBack(Property* property) {
m_map.insert({property->name, property});
m_unqualifiedMap.insert({property->unqualifiedName, property});
m_list.push_back(property);
return property;
}
void BaseObject::Properties::pushFrontAll(Properties* properties) {
m_map.insert(properties->m_map.begin(), properties->m_map.end());
m_unqualifiedMap.insert(properties->m_unqualifiedMap.begin(), properties->m_unqualifiedMap.end());
m_list.insert(m_list.begin(), properties->m_list.begin(), properties->m_list.end());
}
const std::unordered_map<std::string, BaseObject::Property*>& BaseObject::Properties::getMap() const {
return m_map;
}
const std::unordered_map<std::string, BaseObject::Property*>& BaseObject::Properties::getUnqualifiedMap() const {
return m_unqualifiedMap;
}
const std::list<BaseObject::Property*>& BaseObject::Properties::getList() const {
return m_list;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BaseObject::Property
BaseObject::Property::Property(v_int64 pOffset, const char* pName, const Type* pType)
BaseObject::Property::Property(v_int64 pOffset, std::string pName, std::string pUName, const Type* pType)
: offset(pOffset)
, name(pName)
, name(std::move(pName))
, unqualifiedName(std::move(pUName))
, type(pType)
{}
void BaseObject::Property::set(BaseObject* object, const Void& value) {
void BaseObject::Property::set(BaseObject* object, const Void& value) const {
object->set(offset, value);
}
Void BaseObject::Property::get(BaseObject* object) {
Void BaseObject::Property::get(BaseObject* object) const {
return object->get(offset);
}
Void& BaseObject::Property::getAsRef(BaseObject* object) {
Void& BaseObject::Property::getAsRef(BaseObject* object) const {
return object->getAsRef(offset);
}

View File

@ -124,14 +124,21 @@ public:
* Constructor.
* @param pOffset - memory offset of object field from object start address.
* @param pName - name of the property.
* @param pUName - unqualified name of the property. Name of the class field in the code.
* @param pType - &l:Type; of the property.
*/
Property(v_int64 pOffset, const char* pName, const Type* pType);
Property(v_int64 pOffset, std::string pName, std::string pUName, const Type* pType);
/**
* Property name.
*/
const char* const name;
const std::string name;
/**
* Unqualified property name.
* Name of the class field that is used in the code.
*/
const std::string unqualifiedName;
/**
* Property type.
@ -148,21 +155,21 @@ public:
* @param object - object address.
* @param value - value to set.
*/
void set(BaseObject* object, const Void& value);
void set(BaseObject* object, const Void& value) const;
/**
* Get value of object field mapped by this property.
* @param object - object address.
* @return - value of the field.
*/
Void get(BaseObject* object);
Void get(BaseObject* object) const;
/**
* Get reference to ObjectWrapper of the object field.
* @param object - object address.
* @return - reference to ObjectWrapper of the object field.
*/
Void& getAsRef(BaseObject* object);
Void& getAsRef(BaseObject* object) const;
};
@ -172,6 +179,7 @@ public:
class Properties {
private:
std::unordered_map<std::string, Property*> m_map;
std::unordered_map<std::string, Property*> m_unqualifiedMap;
std::list<Property*> m_list;
public:
@ -191,17 +199,19 @@ public:
* Get properties as unordered map for random access.
* @return reference to std::unordered_map of std::string to &id:oatpp::data::type::BaseObject::Property;*.
*/
const std::unordered_map<std::string, Property*>& getMap() const {
return m_map;
}
const std::unordered_map<std::string, Property*>& getMap() const;
/**
* Get properties as unordered map for random access by unqualified names.
* @return reference to std::unordered_map of std::string to &id:oatpp::data::type::BaseObject::Property;*.
*/
const std::unordered_map<std::string, Property*>& getUnqualifiedMap() const;
/**
* Get properties in ordered way.
* @return std::list of &id:oatpp::data::type::BaseObject::Property;*.
*/
const std::list<Property*>& getList() const {
return m_list;
}
const std::list<Property*>& getList() const;
};
@ -425,6 +435,7 @@ private:
public:
typedef oatpp::data::type::Void Void;
typedef oatpp::data::type::Any Any;
typedef oatpp::data::type::Tree Tree;
typedef oatpp::data::type::String String;
typedef oatpp::data::type::Int8 Int8;
typedef oatpp::data::type::UInt8 UInt8;

View File

@ -123,6 +123,27 @@ bool Tree::operator != (const Tree& other) const {
return !operator == (other);
}
mapping::Tree* Tree::operator->() {
if(!m_ptr) {
m_ptr = std::make_shared<mapping::Tree>();
}
return m_ptr.get();
}
mapping::Tree* Tree::operator->() const {
if(!m_ptr) {
throw std::runtime_error("[oatpp::data::type::Tree::operator ->()]: null-pointer exception");
}
return m_ptr.get();
}
mapping::Tree& Tree::operator*() {
if(!m_ptr) {
m_ptr = std::make_shared<mapping::Tree>();
}
return *m_ptr;
}
const mapping::Tree& Tree::operator*() const {
if(!m_ptr) {
throw std::runtime_error("[oatpp::data::type::Tree::operator *()]: null-pointer exception");
@ -130,12 +151,32 @@ const mapping::Tree& Tree::operator*() const {
return *m_ptr;
}
mapping::Tree& Tree::operator*() {
mapping::Tree& Tree::operator [] (const String& key) {
if(!m_ptr) {
throw std::runtime_error("[oatpp::data::type::Tree::operator *()]: null-pointer exception");
m_ptr = std::make_shared<mapping::Tree>();
}
return *m_ptr;
return (*m_ptr)[key];
}
const mapping::Tree& Tree::operator [] (const String& key) const {
if(!m_ptr) {
throw std::runtime_error("[oatpp::data::type::Tree::operator []]: null-pointer exception");
}
return (*m_ptr)[key];
}
mapping::Tree& Tree::operator [] (v_uint64 index) {
if(!m_ptr) {
m_ptr = std::make_shared<mapping::Tree>();
}
return (*m_ptr)[index];
}
const mapping::Tree& Tree::operator [] (v_uint64 index) const {
if(!m_ptr) {
throw std::runtime_error("[oatpp::data::type::Tree::operator []]: null-pointer exception");
}
return (*m_ptr)[index];
}
}}}

View File

@ -26,6 +26,7 @@
#define oatpp_data_type_Tree_hpp
#include "./Type.hpp"
#include "./Primitive.hpp"
namespace oatpp { namespace data { namespace mapping {
@ -108,8 +109,17 @@ public:
bool operator == (const Tree& other) const;
bool operator != (const Tree& other) const;
const mapping::Tree& operator*() const;
mapping::Tree* operator->();
mapping::Tree* operator->() const;
mapping::Tree& operator*();
const mapping::Tree& operator*() const;
mapping::Tree& operator [] (const String& key);
const mapping::Tree& operator [] (const String& key) const;
mapping::Tree& operator [] (v_uint64 index);
const mapping::Tree& operator [] (v_uint64 index) const;
};

View File

@ -86,7 +86,7 @@ oatpp::Void ObjectMapper::read(utils::parser::Caret& caret, const data::type::Ty
/* if expected type is Tree (root element is Tree) - then we can just move deserialized tree */
if(type == data::type::Tree::Class::getType()) {
return oatpp::Tree(tree);
return oatpp::Tree(std::move(tree));
}
{

View File

@ -383,7 +383,7 @@ struct ApiClient::TypeInterpretation<data::type::EnumObjectWrapper<T, I>> {
static oatpp::String toString(const oatpp::String &typeName, const EnumOW &parameter) {
data::type::EnumInterpreterError error = data::type::EnumInterpreterError::OK;
const auto& value = I::toInterpretation(parameter, error);
const auto& value = I::toInterpretation(parameter, false, error);
switch(error){
case data::type::EnumInterpreterError::OK: break;

View File

@ -558,7 +558,7 @@ struct ApiController::TypeInterpretation <data::type::EnumObjectWrapper<T, I>> {
const auto& parsedValue = ApiController::TypeInterpretation<UTOW>::fromString(typeName, text, success);
if(success) {
data::type::EnumInterpreterError error = data::type::EnumInterpreterError::OK;
const auto& result = I::fromInterpretation(parsedValue, error);
const auto& result = I::fromInterpretation(parsedValue, false, error);
if(error == data::type::EnumInterpreterError::OK) {
return result.template cast<EnumOW>();
}

View File

@ -8,6 +8,8 @@ add_executable(oatppAllTests
oatpp/base/CommandLineArgumentsTest.hpp
oatpp/data/buffer/ProcessorTest.cpp
oatpp/data/buffer/ProcessorTest.hpp
oatpp/data/mapping/ObjectRemapperTest.cpp
oatpp/data/mapping/ObjectRemapperTest.hpp
oatpp/data/mapping/ObjectToTreeMapperTest.cpp
oatpp/data/mapping/ObjectToTreeMapperTest.hpp
oatpp/data/mapping/TreeTest.cpp

View File

@ -57,6 +57,7 @@
#include "oatpp/data/mapping/TreeTest.hpp"
#include "oatpp/data/mapping/ObjectToTreeMapperTest.hpp"
#include "oatpp/data/mapping/TreeToObjectMapperTest.hpp"
#include "oatpp/data/mapping/ObjectRemapperTest.hpp"
#include "oatpp/data/share/LazyStringMapTest.hpp"
#include "oatpp/data/share/StringTemplateTest.hpp"
@ -117,6 +118,7 @@ void runTests() {
OATPP_RUN_TEST(oatpp::data::mapping::TreeTest);
OATPP_RUN_TEST(oatpp::data::mapping::ObjectToTreeMapperTest);
OATPP_RUN_TEST(oatpp::data::mapping::TreeToObjectMapperTest);
OATPP_RUN_TEST(oatpp::data::mapping::ObjectRemapperTest);
OATPP_RUN_TEST(oatpp::data::type::ObjectWrapperTest);
OATPP_RUN_TEST(oatpp::data::type::TypeTest);
@ -245,8 +247,6 @@ void runTests() {
}
}
}

View File

@ -0,0 +1,224 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* 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 "ObjectRemapperTest.hpp"
#include "oatpp/data/mapping/ObjectRemapper.hpp"
#include "oatpp/macro/codegen.hpp"
#include <iostream>
namespace oatpp { namespace data { namespace mapping {
namespace {
#include OATPP_CODEGEN_BEGIN(DTO)
ENUM(TestEnum1, v_int32,
VALUE(VALUE_1, 1, "value-1"),
VALUE(VALUE_2, 2, "value-2")
)
ENUM(TestEnum2, v_int32,
VALUE(VALUE_1, 1, "other-value-1"),
VALUE(VALUE_2, 2, "other-value-2")
)
class TestDto1 : public oatpp::DTO {
DTO_INIT(TestDto1, DTO)
DTO_FIELD(String, field1);
DTO_FIELD(String, field2, "field-2");
DTO_FIELD(Enum<TestEnum1>, enum1);
DTO_FIELD(Enum<TestEnum1>, enum2, "enum-2");
DTO_FIELD(Int32 , i32);
};
class TestDto2 : public oatpp::DTO {
DTO_INIT(TestDto2, DTO)
DTO_FIELD(String, field1);
DTO_FIELD(String, field2, "other-field-2");
DTO_FIELD(Enum<TestEnum2>, enum1);
DTO_FIELD(Enum<TestEnum2>, enum2, "other-enum-2");
};
#include OATPP_CODEGEN_END(DTO)
}
void ObjectRemapperTest::onRun() {
ObjectRemapper remapper;
{
OATPP_LOGD(TAG, "Remap. useUnqualifiedFieldNames = false; useUnqualifiedEnumNames = true")
remapper.objectToTreeConfig().useUnqualifiedFieldNames = false;
remapper.treeToObjectConfig().useUnqualifiedFieldNames = false;
remapper.objectToTreeConfig().useUnqualifiedEnumNames = true;
remapper.treeToObjectConfig().useUnqualifiedEnumNames = true;
auto obj1 = TestDto1::createShared();
obj1->field1 = "f1";
obj1->field2 = "f2";
obj1->enum1 = TestEnum1::VALUE_1;
obj1->enum2 = TestEnum1::VALUE_2;
ErrorStack errorStack;
auto obj2 = remapper.remap<oatpp::Object<TestDto2>>(obj1, errorStack);
if(!errorStack.empty()) {
std::cout << *errorStack.stacktrace() << std::endl;
}
OATPP_ASSERT(obj2->field1 == "f1")
OATPP_ASSERT(obj2->field2 == nullptr)
OATPP_ASSERT(obj2->enum1 == TestEnum2::VALUE_1)
OATPP_ASSERT(obj2->enum2 == nullptr)
}
{
OATPP_LOGD(TAG, "Remap. useUnqualifiedFieldNames = true; useUnqualifiedEnumNames = true")
remapper.objectToTreeConfig().useUnqualifiedFieldNames = true;
remapper.treeToObjectConfig().useUnqualifiedFieldNames = true;
remapper.objectToTreeConfig().useUnqualifiedEnumNames = true;
remapper.treeToObjectConfig().useUnqualifiedEnumNames = true;
auto obj1 = TestDto1::createShared();
obj1->field1 = "f1";
obj1->field2 = "f2";
obj1->enum1 = TestEnum1::VALUE_1;
obj1->enum2 = TestEnum1::VALUE_2;
ErrorStack errorStack;
auto obj2 = remapper.remap<oatpp::Object<TestDto2>>(obj1, errorStack);
if(!errorStack.empty()) {
std::cout << *errorStack.stacktrace() << std::endl;
}
OATPP_ASSERT(obj2->field1 == "f1")
OATPP_ASSERT(obj2->field2 == "f2")
OATPP_ASSERT(obj2->enum1 == TestEnum2::VALUE_1)
OATPP_ASSERT(obj2->enum2 == TestEnum2::VALUE_2)
}
{
OATPP_LOGD(TAG, "Remap. Object to Vector")
remapper.objectToTreeConfig().useUnqualifiedFieldNames = false;
remapper.treeToObjectConfig().useUnqualifiedFieldNames = false;
remapper.objectToTreeConfig().useUnqualifiedEnumNames = false;
remapper.treeToObjectConfig().useUnqualifiedEnumNames = false;
auto obj1 = TestDto1::createShared();
obj1->field1 = "f1";
obj1->field2 = "f2";
obj1->enum1 = TestEnum1::VALUE_1;
obj1->enum2 = TestEnum1::VALUE_2;
obj1->i32 = nullptr;
ErrorStack errorStack;
auto vec = remapper.remap<oatpp::Vector<oatpp::String>>(obj1, errorStack);
if(!errorStack.empty()) {
std::cout << *errorStack.stacktrace() << std::endl;
}
OATPP_ASSERT(vec[0] == "f1")
OATPP_ASSERT(vec[1] == "f2")
OATPP_ASSERT(vec[2] == "value-1")
OATPP_ASSERT(vec[3] == "value-2")
OATPP_ASSERT(vec[4] == nullptr)
}
{
OATPP_LOGD(TAG, "Remap tree fragments")
Tree tree;
tree.setVector(3);
tree[0]["field_1"] = "val1";
tree[0]["field_2"] = "val2";
tree[1]["field_1"] = "val1.2";
tree[1]["field_2"] = "val2.2";
tree[2]["field_1"] = "val1.3";
tree[2]["field_2"] = "val2.3";
{
auto map = remapper.remap<oatpp::UnorderedFields<String>>(tree[0]);
OATPP_ASSERT(map->size() == 2)
OATPP_ASSERT(map["field_1"] == "val1")
OATPP_ASSERT(map["field_2"] == "val2")
}
{
auto map = remapper.remap<oatpp::UnorderedFields<String>>(tree[1]);
OATPP_ASSERT(map->size() == 2)
OATPP_ASSERT(map["field_1"] == "val1.2")
OATPP_ASSERT(map["field_2"] == "val2.2")
}
{
auto map = remapper.remap<oatpp::UnorderedFields<String>>(tree[2]);
OATPP_ASSERT(map->size() == 2)
OATPP_ASSERT(map["field_1"] == "val1.3")
OATPP_ASSERT(map["field_2"] == "val2.3")
}
}
{
oatpp::Tree tree;
OATPP_ASSERT(tree == nullptr)
OATPP_ASSERT(tree->isUndefined()) // implicitly initialized
OATPP_ASSERT(tree != nullptr)
tree["hello"] = "world";
std::cout << *tree->debugPrint() << std::endl;
}
}
}}}

View File

@ -0,0 +1,40 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* 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_data_mapping_ObjectRemapperTest_hpp
#define oatpp_data_mapping_ObjectRemapperTest_hpp
#include "oatpp-test/UnitTest.hpp"
namespace oatpp { namespace data { namespace mapping {
class ObjectRemapperTest : public oatpp::test::UnitTest {
public:
ObjectRemapperTest() : UnitTest("TEST[oatpp::data::mapping::ObjectRemapperTest]") {}
void onRun() override;
};
}}}
#endif /* oatpp_data_mapping_ObjectRemapperTest_hpp */

View File

@ -36,27 +36,32 @@ namespace {
#include OATPP_CODEGEN_BEGIN(DTO)
ENUM(TestEnum, v_int32,
VALUE(VALUE_1, 1, "value-1"),
VALUE(VALUE_2, 2, "value-2")
)
class TestDto1 : public oatpp::DTO {
DTO_INIT(TestDto1, DTO)
DTO_FIELD(String, str);
DTO_FIELD(String, str, "str-q");
DTO_FIELD(Int8, i8);
DTO_FIELD(UInt8, ui8);
DTO_FIELD(Int8, i8, "i8-q");
DTO_FIELD(UInt8, ui8, "ui8-q");
DTO_FIELD(Int16, i16);
DTO_FIELD(UInt16, ui16);
DTO_FIELD(Int16, i16, "i16-q");
DTO_FIELD(UInt16, ui16, "ui16-q");
DTO_FIELD(Int32, i32);
DTO_FIELD(UInt32, ui32);
DTO_FIELD(Int32, i32, "i32-q");
DTO_FIELD(UInt32, ui32, "ui32-q");
DTO_FIELD(Int64, i64);
DTO_FIELD(UInt64, ui64);
DTO_FIELD(Int64, i64, "i64-q");
DTO_FIELD(UInt64, ui64, "ui64-q");
DTO_FIELD(Vector<oatpp::Object<TestDto1>>, vector);
DTO_FIELD(UnorderedFields<oatpp::Object<TestDto1>>, map);
DTO_FIELD(Fields<String>, pairs);
DTO_FIELD(Vector<oatpp::Object<TestDto1>>, vector, "vector-q");
DTO_FIELD(UnorderedFields<oatpp::Object<TestDto1>>, map, "map-q");
DTO_FIELD(Fields<String>, pairs, "pairs-q");
};
@ -105,7 +110,79 @@ void ObjectToTreeMapperTest::onRun() {
}
{
OATPP_LOGD(TAG, "Map Object")
OATPP_LOGD(TAG, "Map Object qualified")
config.useUnqualifiedFieldNames = false;
Tree tree;
ObjectToTreeMapper::State state;
state.tree = &tree;
state.config = &config;
auto obj = TestDto1::createShared();
obj->str = "hello object";
obj->i8 = -8;
obj->ui8 = 8;
obj->i16 = -16;
obj->ui16 = 16;
obj->i32 = -32;
obj->ui32 = 32;
obj->i64 = -64;
obj->ui64 = 64;
obj->vector = {TestDto1::createShared(), TestDto1::createShared(), TestDto1::createShared()};
obj->map = {{"key1", TestDto1::createShared()}, {"key2", TestDto1::createShared()}, {"key3", TestDto1::createShared()}};
obj->pairs = {{"same-key", "value1"}, {"same-key", "value2"}, {"same-key", "value3"}};
obj->vector[0]->str = "vec-item-1";
obj->vector[1]->str = "vec-item-2";
obj->vector[2]->str = "vec-item-3";
obj->map["key1"]->i64 = 1;
obj->map["key2"]->i64 = 2;
obj->map["key3"]->i64 = 3;
mapper.map(state, obj);
OATPP_ASSERT(state.errorStack.empty())
std::cout << *tree.debugPrint() << std::endl;
OATPP_ASSERT(tree.getType() == Tree::Type::MAP)
OATPP_ASSERT(tree["str-q"].getString() == "hello object")
OATPP_ASSERT(tree["i8-q"].getValue<v_int8>() == -8)
OATPP_ASSERT(tree["ui8-q"].getValue<v_uint8>() == 8)
OATPP_ASSERT(tree["i16-q"].getValue<v_int16>() == -16)
OATPP_ASSERT(tree["ui16-q"].getValue<v_uint16>() == 16)
OATPP_ASSERT(tree["i32-q"].getValue<v_int32>() == -32)
OATPP_ASSERT(tree["ui32-q"].getValue<v_uint32>() == 32)
OATPP_ASSERT(tree["i64-q"].getValue<v_int64>() == -64)
OATPP_ASSERT(tree["ui64-q"].getValue<v_uint64>() == 64)
OATPP_ASSERT(tree["vector-q"].getVector().size() == 3)
OATPP_ASSERT(tree["vector-q"][0]["str-q"].getString() == "vec-item-1")
OATPP_ASSERT(tree["vector-q"][1]["str-q"].getString() == "vec-item-2")
OATPP_ASSERT(tree["vector-q"][2]["str-q"].getString() == "vec-item-3")
OATPP_ASSERT(tree["map-q"].getMap().size() == 3)
OATPP_ASSERT(tree["map-q"]["key1"]["i64-q"].getValue<v_int64>() == 1)
OATPP_ASSERT(tree["map-q"]["key2"]["i64-q"].getValue<v_int64>() == 2)
OATPP_ASSERT(tree["map-q"]["key3"]["i64-q"].getValue<v_int64>() == 3)
auto& pairs = tree["pairs-q"].getPairs();
OATPP_ASSERT(pairs.size() == 3)
OATPP_ASSERT(pairs[0].first == "same-key" && pairs[0].second.getString() == "value1")
OATPP_ASSERT(pairs[1].first == "same-key" && pairs[1].second.getString() == "value2")
OATPP_ASSERT(pairs[2].first == "same-key" && pairs[2].second.getString() == "value3")
}
{
OATPP_LOGD(TAG, "Map Object unqualified")
config.useUnqualifiedFieldNames = true;
Tree tree;
ObjectToTreeMapper::State state;
state.tree = &tree;
@ -172,6 +249,42 @@ void ObjectToTreeMapperTest::onRun() {
}
{
OATPP_LOGD(TAG, "Map Enum qualified")
config.useUnqualifiedEnumNames = false;
Tree tree;
ObjectToTreeMapper::State state;
state.tree = &tree;
state.config = &config;
oatpp::Enum<TestEnum> enumObj(TestEnum::VALUE_1);
mapper.map(state, enumObj);
std::cout << *tree.debugPrint() << std::endl;
OATPP_ASSERT(tree.getString() == "value-1");
}
{
OATPP_LOGD(TAG, "Map Enum Unqualified")
config.useUnqualifiedEnumNames = true;
Tree tree;
ObjectToTreeMapper::State state;
state.tree = &tree;
state.config = &config;
oatpp::Enum<TestEnum> enumObj = oatpp::Enum<TestEnum>::getEntryByUnqualifiedName("VALUE_2").value;
mapper.map(state, enumObj);
std::cout << *tree.debugPrint() << std::endl;
OATPP_ASSERT(tree.getString() == "VALUE_2");
}
}
}}}

View File

@ -36,6 +36,8 @@ namespace {
template<typename T>
void testTreeValue(T value) {
OATPP_LOGD("TEST", "Test value retrieval for '%s'", Tree::NodePrimitiveType<T>::name)
Tree node;
//node.setValue<T>(value);
@ -70,6 +72,7 @@ void TreeTest::onRun() {
testTreeValue<v_float64>(16);
{
OATPP_LOGD(TAG, "Case 1")
Tree node;
oatpp::String original = "Hello World!";
node.setString(original);
@ -79,6 +82,7 @@ void TreeTest::onRun() {
}
{
OATPP_LOGD(TAG, "Case 2")
Tree node1;
Tree node2;
@ -93,6 +97,7 @@ void TreeTest::onRun() {
}
{
OATPP_LOGD(TAG, "Case 3")
Tree node1;
Tree node2;
@ -105,6 +110,7 @@ void TreeTest::onRun() {
}
{
OATPP_LOGD(TAG, "Case 4")
std::vector<Tree> originalVector(10);
for(v_uint32 i = 0; i < 10; i ++) {
originalVector.at(i).setValue(i);
@ -132,6 +138,7 @@ void TreeTest::onRun() {
}
{
OATPP_LOGD(TAG, "Case 5")
TreeMap originalMap;
for(v_uint32 i = 0; i < 10; i ++) {
originalMap["node_" + utils::Conversion::int32ToStr(static_cast<v_int32>(i))].setValue(i);
@ -156,6 +163,7 @@ void TreeTest::onRun() {
}
{
OATPP_LOGD(TAG, "Case 6")
Tree article;
oatpp::Tree ot;
@ -176,6 +184,120 @@ void TreeTest::onRun() {
}
{
OATPP_LOGD(TAG, "Attributes Case 1")
OATPP_LOGD(TAG, "size of Tree::Attributes='%lu'", sizeof(Tree::Attributes))
Tree::Attributes attr;
attr["key1"] = "value1";
attr["key1"] = "value1.2";
attr["key2"] = "value2";
OATPP_ASSERT(attr.size() == 2)
OATPP_ASSERT(attr["key1"] == "value1.2")
OATPP_ASSERT(attr["key2"] == "value2")
OATPP_ASSERT(attr["key3"] == nullptr) // key3 added
OATPP_ASSERT(attr.size() == 3)
OATPP_ASSERT(attr[0].second.get() == "value1.2")
OATPP_ASSERT(attr[1].second.get() == "value2")
OATPP_ASSERT(attr[2].second.get() == nullptr)
}
{
OATPP_LOGD(TAG, "Attributes Case 2")
Tree::Attributes attr1;
Tree::Attributes attr2;
attr1["key1"] = "value1";
attr1["key2"] = "value2";
attr1["key3"] = nullptr;
attr2["key1"] = "v1";
attr2["key2"] = "v2";
attr2 = attr1;
attr1["key1"] = "1";
attr1["key2"] = "2";
attr1["key3"] = "3";
OATPP_ASSERT(attr1[0].second.get() == "1")
OATPP_ASSERT(attr1[1].second.get() == "2")
OATPP_ASSERT(attr1[2].second.get() == "3")
OATPP_ASSERT(attr1["key1"] == "1")
OATPP_ASSERT(attr1["key2"] == "2")
OATPP_ASSERT(attr1["key3"] == "3")
OATPP_ASSERT(attr2[0].second.get() == "value1")
OATPP_ASSERT(attr2[1].second.get() == "value2")
OATPP_ASSERT(attr2[2].second.get() == nullptr)
OATPP_ASSERT(attr2["key1"] == "value1")
OATPP_ASSERT(attr2["key2"] == "value2")
OATPP_ASSERT(attr2["key3"] == nullptr)
Tree::Attributes attr3;
attr2 = attr3;
OATPP_ASSERT(attr2.empty())
}
{
OATPP_LOGD(TAG, "Attributes Case 3")
Tree tree1;
Tree tree2;
tree1 = "hello";
tree2 = "world";
tree1.attributes()["key1"] = "value1";
tree1.attributes()["key2"] = "value2";
tree1.attributes()["key3"] = nullptr;
tree2.attributes()["key1"] = "v1";
tree2.attributes()["key2"] = "v2";
tree2 = tree1;
tree1.attributes()["key1"] = "1";
tree1.attributes()["key2"] = "2";
tree1.attributes()["key3"] = "3";
OATPP_ASSERT(tree1.attributes()[0].second.get() == "1")
OATPP_ASSERT(tree1.attributes()[1].second.get() == "2")
OATPP_ASSERT(tree1.attributes()[2].second.get() == "3")
OATPP_ASSERT(tree1.attributes()["key1"] == "1")
OATPP_ASSERT(tree1.attributes()["key2"] == "2")
OATPP_ASSERT(tree1.attributes()["key3"] == "3")
OATPP_ASSERT(tree2.attributes()[0].second.get() == "value1")
OATPP_ASSERT(tree2.attributes()[1].second.get() == "value2")
OATPP_ASSERT(tree2.attributes()[2].second.get() == nullptr)
OATPP_ASSERT(tree2.attributes()["key1"] == "value1")
OATPP_ASSERT(tree2.attributes()["key2"] == "value2")
OATPP_ASSERT(tree2.attributes()["key3"] == nullptr)
Tree tree3;
tree2 = tree3;
OATPP_ASSERT(tree2.attributes().empty())
}
}
}}}

View File

@ -38,27 +38,32 @@ namespace {
#include OATPP_CODEGEN_BEGIN(DTO)
ENUM(TestEnum, v_int32,
VALUE(VALUE_1, 1, "value-1"),
VALUE(VALUE_2, 2, "value-2")
)
class TestDto1 : public oatpp::DTO {
DTO_INIT(TestDto1, DTO)
DTO_FIELD(String, str);
DTO_FIELD(String, str, "str-q");
DTO_FIELD(Int8, i8);
DTO_FIELD(UInt8, ui8);
DTO_FIELD(Int8, i8, "i8-q");
DTO_FIELD(UInt8, ui8, "ui8-q");
DTO_FIELD(Int16, i16);
DTO_FIELD(UInt16, ui16);
DTO_FIELD(Int16, i16, "i16-q");
DTO_FIELD(UInt16, ui16, "ui16-q");
DTO_FIELD(Int32, i32);
DTO_FIELD(UInt32, ui32);
DTO_FIELD(Int32, i32, "i32-q");
DTO_FIELD(UInt32, ui32, "ui32-q");
DTO_FIELD(Int64, i64);
DTO_FIELD(UInt64, ui64);
DTO_FIELD(Int64, i64, "i64-q");
DTO_FIELD(UInt64, ui64, "ui64-q");
DTO_FIELD(Vector<oatpp::Object<TestDto1>>, vector);
DTO_FIELD(UnorderedFields<oatpp::Object<TestDto1>>, map);
DTO_FIELD(Fields<String>, pairs);
DTO_FIELD(Vector<oatpp::Object<TestDto1>>, vector, "vector-q");
DTO_FIELD(UnorderedFields<oatpp::Object<TestDto1>>, map, "map-q");
DTO_FIELD(Fields<String>, pairs, "pairs-q");
};
@ -79,6 +84,86 @@ void TreeToObjectMapperTest::onRun() {
ObjectToTreeMapper::Config reverseConfig;
{
OATPP_LOGD(TAG, "Map Object qualified")
config.useUnqualifiedFieldNames = false;
Tree tree;
tree["str-q"] = "Hello World!";
tree["i8-q"] = -8;
tree["ui8-q"] = 8;
tree["i16-q"] = -16;
tree["ui16-q"] = 16;
tree["i32-q"] = -32;
tree["ui32-q"] = 32;
tree["i64-q"] = -64;
tree["ui64-q"] = 64;
tree["vector-q"].setVector(3);
tree["vector-q"][0]["str-q"] = "nested_1 (in vector)";
tree["vector-q"][1]["str-q"] = "nested_2 (in vector)";
tree["vector-q"][2]["str-q"] = "nested_3 (in vector)";
tree["map-q"]["nested_1"]["i32-q"] = 1;
tree["map-q"]["nested_2"]["i32-q"] = 2;
tree["map-q"]["nested_3"]["i32-q"] = 3;
auto& pairs = tree["pairs-q"].getPairs();
pairs.push_back({"same-key", {}});
pairs.push_back({"same-key", {}});
pairs.push_back({"same-key", {}});
pairs[0].second = "value1";
pairs[1].second = "value2";
pairs[2].second = "value3";
TreeToObjectMapper::State state;
state.config = &config;
state.tree = &tree;
const auto& polymorph = mapper.map(state,oatpp::Object<TestDto1>::Class::getType());
if(state.errorStack.empty()) {
auto json = jsonMapper.writeToString(polymorph);
std::cout << *json << std::endl;
} else {
auto err = state.errorStack.stacktrace();
std::cout << *err << std::endl;
}
auto obj = polymorph.cast<oatpp::Object<TestDto1>>();
OATPP_ASSERT(obj->str == "Hello World!")
OATPP_ASSERT(obj->i8 == -8)
OATPP_ASSERT(obj->ui8 == 8)
OATPP_ASSERT(obj->i16 == -16)
OATPP_ASSERT(obj->ui16 == 16)
OATPP_ASSERT(obj->i32 == -32)
OATPP_ASSERT(obj->ui32 == 32)
OATPP_ASSERT(obj->i64 == -64)
OATPP_ASSERT(obj->ui64 == 64)
OATPP_ASSERT(obj->vector->size() == 3)
OATPP_ASSERT(obj->vector[0]->str == "nested_1 (in vector)")
OATPP_ASSERT(obj->vector[1]->str == "nested_2 (in vector)")
OATPP_ASSERT(obj->vector[2]->str == "nested_3 (in vector)")
OATPP_ASSERT(obj->map->size() == 3)
OATPP_ASSERT(obj->map["nested_1"]->i32 = 1)
OATPP_ASSERT(obj->map["nested_2"]->i32 = 2)
OATPP_ASSERT(obj->map["nested_3"]->i32 = 3)
OATPP_ASSERT(obj->pairs->size() == 3)
OATPP_ASSERT(obj->pairs[0].first == "same-key" && obj->pairs[0].second == "value1")
OATPP_ASSERT(obj->pairs[1].first == "same-key" && obj->pairs[1].second == "value2")
OATPP_ASSERT(obj->pairs[2].first == "same-key" && obj->pairs[2].second == "value3")
}
{
OATPP_LOGD(TAG, "Map Object unqualified")
config.useUnqualifiedFieldNames = true;
Tree tree;
tree["str"] = "Hello World!";
@ -152,6 +237,52 @@ void TreeToObjectMapperTest::onRun() {
}
{
OATPP_LOGD(TAG, "Map Enum qualified")
config.useUnqualifiedEnumNames = false;
Tree tree;
TreeToObjectMapper::State state;
state.tree = &tree;
state.config = &config;
tree = "value-2";
auto polymorph = mapper.map(state, oatpp::Enum<TestEnum>::Class::getType());
if(!state.errorStack.empty()) {
auto err = state.errorStack.stacktrace();
std::cout << *err << std::endl;
}
OATPP_ASSERT(polymorph.getValueType() == oatpp::Enum<TestEnum>::Class::getType())
OATPP_ASSERT(polymorph.cast<oatpp::Enum<TestEnum>>() == TestEnum::VALUE_2)
}
{
OATPP_LOGD(TAG, "Map Enum unqualified")
config.useUnqualifiedEnumNames = true;
Tree tree;
TreeToObjectMapper::State state;
state.tree = &tree;
state.config = &config;
tree = "VALUE_2";
auto polymorph = mapper.map(state, oatpp::Enum<TestEnum>::Class::getType());
if(!state.errorStack.empty()) {
auto err = state.errorStack.stacktrace();
std::cout << *err << std::endl;
}
OATPP_ASSERT(polymorph.getValueType() == oatpp::Enum<TestEnum>::Class::getType())
OATPP_ASSERT(polymorph.cast<oatpp::Enum<TestEnum>>() == TestEnum::VALUE_2)
}
}
}}}

View File

@ -154,7 +154,7 @@ void EnumTest::onRun() {
OATPP_ASSERT(pd2->notNull == true)
{
auto interEnum = pd1->getInterpretedEnum();
auto interEnum = pd1->getInterpretedEnum(false);
OATPP_ASSERT(interEnum.size() == 3)
OATPP_ASSERT(interEnum[0].getStoredType() == oatpp::String::Class::getType())
}
@ -163,16 +163,16 @@ void EnumTest::onRun() {
}
{
OATPP_LOGI(TAG, "Test Interpreter AsString...")
OATPP_LOGI(TAG, "Test Interpreter AsString (Qualified)...")
oatpp::data::type::EnumInterpreterError e = oatpp::data::type::EnumInterpreterError::OK;
auto inter = oatpp::Enum<Enum2>::AsString::Interpreter::toInterpretation(oatpp::Enum<Enum2>::AsString(Enum2::NAME_1), e);
auto inter = oatpp::Enum<Enum2>::AsString::Interpreter::toInterpretation(oatpp::Enum<Enum2>::AsString(Enum2::NAME_1), false, e);
OATPP_ASSERT(inter.getValueType() == oatpp::String::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)
auto interValue = inter.cast<oatpp::String>();
OATPP_ASSERT(interValue == "name-1")
oatpp::Void voidValue = oatpp::Enum<Enum2>::AsString::Interpreter::fromInterpretation(interValue, e);
oatpp::Void voidValue = oatpp::Enum<Enum2>::AsString::Interpreter::fromInterpretation(interValue, false, e);
OATPP_ASSERT(voidValue.getValueType() == oatpp::Enum<Enum2>::AsString::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)
@ -182,16 +182,54 @@ void EnumTest::onRun() {
}
{
OATPP_LOGI(TAG, "Test Interpreter AsNumber...")
OATPP_LOGI(TAG, "Test Interpreter AsString (Unqualified)...")
oatpp::data::type::EnumInterpreterError e = oatpp::data::type::EnumInterpreterError::OK;
auto inter = oatpp::Enum<Enum2>::AsNumber::Interpreter::toInterpretation(oatpp::Enum<Enum2>::AsNumber(Enum2::NAME_1), e);
auto inter = oatpp::Enum<Enum2>::AsString::Interpreter::toInterpretation(oatpp::Enum<Enum2>::AsString(Enum2::NAME_1), true, e);
OATPP_ASSERT(inter.getValueType() == oatpp::String::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)
auto interValue = inter.cast<oatpp::String>();
OATPP_ASSERT(interValue == "NAME_1")
oatpp::Void voidValue = oatpp::Enum<Enum2>::AsString::Interpreter::fromInterpretation(interValue, true, e);
OATPP_ASSERT(voidValue.getValueType() == oatpp::Enum<Enum2>::AsString::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)
auto value = voidValue.cast<oatpp::Enum<Enum2>::AsString>();
OATPP_ASSERT(value == Enum2::NAME_1)
OATPP_LOGI(TAG, "OK")
}
{
OATPP_LOGI(TAG, "Test Interpreter AsNumber (Qualified)...")
oatpp::data::type::EnumInterpreterError e = oatpp::data::type::EnumInterpreterError::OK;
auto inter = oatpp::Enum<Enum2>::AsNumber::Interpreter::toInterpretation(oatpp::Enum<Enum2>::AsNumber(Enum2::NAME_1), false, e);
OATPP_ASSERT(inter.getValueType() == oatpp::Int32::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)
auto interValue = inter.cast<oatpp::Int32>();
OATPP_ASSERT(interValue == static_cast<v_int32>(Enum2::NAME_1))
oatpp::Void voidValue = oatpp::Enum<Enum2>::AsNumber::Interpreter::fromInterpretation(interValue, e);
oatpp::Void voidValue = oatpp::Enum<Enum2>::AsNumber::Interpreter::fromInterpretation(interValue, false, e);
OATPP_ASSERT(voidValue.getValueType() == oatpp::Enum<Enum2>::AsNumber::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)
auto value = voidValue.cast<oatpp::Enum<Enum2>::AsNumber>();
OATPP_ASSERT(value == Enum2::NAME_1)
OATPP_LOGI(TAG, "OK")
}
{
OATPP_LOGI(TAG, "Test Interpreter AsNumber (Unualified)...")
oatpp::data::type::EnumInterpreterError e = oatpp::data::type::EnumInterpreterError::OK;
auto inter = oatpp::Enum<Enum2>::AsNumber::Interpreter::toInterpretation(oatpp::Enum<Enum2>::AsNumber(Enum2::NAME_1), true, e);
OATPP_ASSERT(inter.getValueType() == oatpp::Int32::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)
auto interValue = inter.cast<oatpp::Int32>();
OATPP_ASSERT(interValue == static_cast<v_int32>(Enum2::NAME_1))
oatpp::Void voidValue = oatpp::Enum<Enum2>::AsNumber::Interpreter::fromInterpretation(interValue, true, e);
OATPP_ASSERT(voidValue.getValueType() == oatpp::Enum<Enum2>::AsNumber::Class::getType())
OATPP_ASSERT(e == oatpp::data::type::EnumInterpreterError::OK)