Tree: introduce ObjectToTreeMapper and TreeToObjectMapper. WIP

This commit is contained in:
Leonid Stryzhevskyi 2024-04-28 06:11:20 +03:00
parent 4a7795c221
commit 1d64355eef
11 changed files with 1208 additions and 23 deletions

View File

@ -62,8 +62,12 @@ add_library(oatpp
oatpp/concurrency/Utils.hpp
oatpp/data/Bundle.cpp
oatpp/data/Bundle.hpp
oatpp/data/ObjectToTreeMapper.cpp
oatpp/data/ObjectToTreeMapper.hpp
oatpp/data/Tree.cpp
oatpp/data/Tree.hpp
oatpp/data/TreeToObjectMapper.cpp
oatpp/data/TreeToObjectMapper.hpp
oatpp/data/buffer/FIFOBuffer.cpp
oatpp/data/buffer/FIFOBuffer.hpp
oatpp/data/buffer/IOBuffer.cpp

View File

@ -0,0 +1,301 @@
/***************************************************************************
*
* 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 "ObjectToTreeMapper.hpp"
#include "oatpp/data/stream/BufferStream.hpp"
#include "oatpp/utils/Conversion.hpp"
namespace oatpp { namespace data {
oatpp::String ObjectToTreeMapper::MappingState::errorStacktrace() const {
stream::BufferOutputStream ss;
for(auto& s : errorStack) {
ss << s << "\n";
}
return ss.toString();
}
ObjectToTreeMapper::ObjectToTreeMapper() {
m_methods.resize(static_cast<size_t>(data::mapping::type::ClassId::getClassCount()), nullptr);
setMapperMethod(data::mapping::type::__class::String::CLASS_ID, &ObjectToTreeMapper::mapString);
setMapperMethod(data::mapping::type::__class::Any::CLASS_ID, &ObjectToTreeMapper::mapAny);
setMapperMethod(data::mapping::type::__class::Int8::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::Int8>);
setMapperMethod(data::mapping::type::__class::UInt8::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::UInt8>);
setMapperMethod(data::mapping::type::__class::Int16::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::Int16>);
setMapperMethod(data::mapping::type::__class::UInt16::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::UInt16>);
setMapperMethod(data::mapping::type::__class::Int32::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::Int32>);
setMapperMethod(data::mapping::type::__class::UInt32::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::UInt32>);
setMapperMethod(data::mapping::type::__class::Int64::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::Int64>);
setMapperMethod(data::mapping::type::__class::UInt64::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::UInt64>);
setMapperMethod(data::mapping::type::__class::Float32::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::Float32>);
setMapperMethod(data::mapping::type::__class::Float64::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::Float64>);
setMapperMethod(data::mapping::type::__class::Boolean::CLASS_ID, &ObjectToTreeMapper::mapPrimitive<oatpp::Boolean>);
setMapperMethod(data::mapping::type::__class::AbstractObject::CLASS_ID, &ObjectToTreeMapper::mapObject);
setMapperMethod(data::mapping::type::__class::AbstractEnum::CLASS_ID, &ObjectToTreeMapper::mapEnum);
setMapperMethod(data::mapping::type::__class::AbstractVector::CLASS_ID, &ObjectToTreeMapper::mapCollection);
setMapperMethod(data::mapping::type::__class::AbstractList::CLASS_ID, &ObjectToTreeMapper::mapCollection);
setMapperMethod(data::mapping::type::__class::AbstractUnorderedSet::CLASS_ID, &ObjectToTreeMapper::mapCollection);
setMapperMethod(data::mapping::type::__class::AbstractPairList::CLASS_ID, &ObjectToTreeMapper::mapMap);
setMapperMethod(data::mapping::type::__class::AbstractUnorderedMap::CLASS_ID, &ObjectToTreeMapper::mapMap);
}
void ObjectToTreeMapper::setMapperMethod(const data::mapping::type::ClassId& classId, MapperMethod method) {
const auto id = static_cast<v_uint32>(classId.id);
if(id >= m_methods.size()) {
m_methods.resize(id + 1, nullptr);
}
m_methods[id] = method;
}
void ObjectToTreeMapper::map(MappingState& state, const oatpp::Void& polymorph)
{
auto id = static_cast<v_uint32>(polymorph.getValueType()->classId.id);
auto& method = m_methods[id];
if(method) {
(*method)(this, state, polymorph);
} else {
auto* interpretation = polymorph.getValueType()->findInterpretation(state.config->enabledInterpretations);
if(interpretation) {
map(state, interpretation->toInterpretation(polymorph));
} else {
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::map()]: "
"Error. No serialize method for type '" +
oatpp::String(polymorph.getValueType()->classId.name) + "'");
return;
}
}
}
void ObjectToTreeMapper::mapString(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph) {
if(!polymorph) {
state.tree->setNull();
return;
}
state.tree->setString(oatpp::String(std::static_pointer_cast<std::string>(polymorph.getPtr()), oatpp::String::Class::getType()));
}
void ObjectToTreeMapper::mapAny(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph) {
if(!polymorph) {
state.tree->setNull();
return;
}
auto anyHandle = static_cast<data::mapping::type::AnyHandle*>(polymorph.get());
mapper->map(state, oatpp::Void(anyHandle->ptr, anyHandle->type));
}
void ObjectToTreeMapper::mapEnum(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph) {
if(!polymorph) {
state.tree->setNull();
return;
}
auto polymorphicDispatcher = static_cast<const data::mapping::type::__class::AbstractEnum::PolymorphicDispatcher*>(
polymorph.getValueType()->polymorphicDispatcher
);
data::mapping::type::EnumInterpreterError e = data::mapping::type::EnumInterpreterError::OK;
mapper->map(state, polymorphicDispatcher->toInterpretation(polymorph, e));
if(e == data::mapping::type::EnumInterpreterError::OK) {
return;
}
switch(e) {
case data::mapping::type::EnumInterpreterError::CONSTRAINT_NOT_NULL:
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::mapEnum()]: Error. Enum constraint violated - 'NotNull'.");
break;
case data::mapping::type::EnumInterpreterError::OK:
case data::mapping::type::EnumInterpreterError::TYPE_MISMATCH_ENUM:
case data::mapping::type::EnumInterpreterError::TYPE_MISMATCH_ENUM_VALUE:
case data::mapping::type::EnumInterpreterError::ENTRY_NOT_FOUND:
default:
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::mapEnum()]: Error. Can't serialize Enum.");
}
}
void ObjectToTreeMapper::mapCollection(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph) {
if(!polymorph) {
state.tree->setNull();
return;
}
auto dispatcher = static_cast<const data::mapping::type::__class::Collection::PolymorphicDispatcher*>(
polymorph.getValueType()->polymorphicDispatcher
);
auto iterator = dispatcher->beginIteration(polymorph);
state.tree->setVector(0);
auto& vector = state.tree->getVector();
v_int64 index = 0;
while (!iterator->finished()) {
const auto& value = iterator->get();
if(value || state.config->includeNullFields || state.config->alwaysIncludeNullCollectionElements) {
vector.emplace_back();
MappingState nestedState;
nestedState.tree = &vector[vector.size() - 1];
nestedState.config = state.config;
mapper->map(nestedState, value);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(state.errorStack.end(), nestedState.errorStack);
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::mapCollection()]: index=" + utils::Conversion::int64ToStr(index));
return;
}
}
iterator->next();
index ++;
}
}
void ObjectToTreeMapper::mapMap(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph) {
if(!polymorph) {
state.tree->setNull();
return;
}
auto dispatcher = static_cast<const data::mapping::type::__class::Map::PolymorphicDispatcher*>(
polymorph.getValueType()->polymorphicDispatcher
);
auto keyType = dispatcher->getKeyType();
if(keyType->classId != oatpp::String::Class::CLASS_ID){
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::mapMap()]: Invalid map key. Key should be String");
return;
}
auto iterator = dispatcher->beginIteration(polymorph);
state.tree->setMap({});
auto& map = state.tree->getMap();
while (!iterator->finished()) {
const auto& value = iterator->getValue();
if(value || state.config->includeNullFields || state.config->alwaysIncludeNullCollectionElements) {
const auto& untypedKey = iterator->getKey();
const auto& key = oatpp::String(std::static_pointer_cast<std::string>(untypedKey.getPtr()));
MappingState nestedState;
nestedState.tree = &map[key];
nestedState.config = state.config;
mapper->map(nestedState, value);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(state.errorStack.end(), nestedState.errorStack);
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::mapMap()]: key='" + key + "'");
return;
}
}
iterator->next();
}
}
void ObjectToTreeMapper::mapObject(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph) {
if(!polymorph) {
state.tree->setNull();
return;
}
auto type = polymorph.getValueType();
auto dispatcher = static_cast<const oatpp::data::mapping::type::__class::AbstractObject::PolymorphicDispatcher*>(
type->polymorphicDispatcher
);
auto fields = dispatcher->getProperties()->getList();
auto object = static_cast<oatpp::BaseObject*>(polymorph.get());
state.tree->setMap({});
auto& map = state.tree->getMap();
for (auto const& field : fields) {
oatpp::Void value;
if(field->info.typeSelector && field->type == oatpp::Any::Class::getType()) {
const auto& any = field->get(object).cast<oatpp::Any>();
value = any.retrieve(field->info.typeSelector->selectType(object));
} else {
value = field->get(object);
}
if(field->info.required && value == nullptr) {
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::mapObject()]: "
"Error. " + std::string(type->nameQualifier) + "::"
+ std::string(field->name) + " is required!");
return;
}
if (value || state.config->includeNullFields || (field->info.required && state.config->alwaysIncludeRequired)) {
MappingState nestedState;
nestedState.tree = &map[oatpp::String(field->name)];
nestedState.config = state.config;
mapper->map(nestedState, value);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(state.errorStack.end(), nestedState.errorStack);
state.errorStack.emplace_back("[oatpp::data::ObjectToTreeMapper::mapObject()]: field='" + oatpp::String(field->name) + "'");
return;
}
}
}
}
}}

View File

@ -0,0 +1,91 @@
/***************************************************************************
*
* 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_ObjectToTreeMapper_hpp
#define oatpp_data_ObjectToTreeMapper_hpp
#include "./Tree.hpp"
namespace oatpp { namespace data {
class ObjectToTreeMapper : public base::Countable {
public:
struct Config {
bool includeNullFields = true;
bool alwaysIncludeRequired = false;
bool alwaysIncludeNullCollectionElements = false;
std::vector<std::string> enabledInterpretations = {};
};
public:
struct MappingState {
Config* config;
Tree* tree;
std::list<oatpp::String> errorStack;
oatpp::String errorStacktrace() const;
};
public:
typedef void (*MapperMethod)(ObjectToTreeMapper*, MappingState&, const oatpp::Void&);
public:
template<class T>
static void mapPrimitive(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph){
(void) mapper;
if(polymorph){
state.tree->setValue<typename T::ObjectType>(* static_cast<typename T::ObjectType*>(polymorph.get()));
} else {
state.tree->setNull();
}
}
static void mapString(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph);
static void mapAny(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph);
static void mapEnum(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph);
static void mapCollection(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph);
static void mapMap(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph);
static void mapObject(ObjectToTreeMapper* mapper, MappingState& state, const oatpp::Void& polymorph);
private:
std::vector<MapperMethod> m_methods;
public:
ObjectToTreeMapper();
void setMapperMethod(const data::mapping::type::ClassId& classId, MapperMethod method);
void map(MappingState& state, const oatpp::Void& polymorph);
};
}}
#endif //oatpp_data_ObjectToTreeMapper_hpp

View File

@ -24,6 +24,8 @@
#include "Tree.hpp"
#include "oatpp/data/stream/BufferStream.hpp"
namespace oatpp { namespace data {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -495,4 +497,122 @@ Tree::Map& Tree::getMap() {
return *data;
}
oatpp::String Tree::debugPrint(v_uint32 indent0, v_uint32 indentDelta, bool firstLineIndent) const {
stream::BufferOutputStream ss;
for(v_uint32 i = 0; i < indent0; i ++) {
ss << " ";
}
oatpp::String indentStr0 = ss.toString();
ss.setCurrentPosition(0);
for(v_uint32 i = 0; i < indentDelta; i ++) {
ss << " ";
}
oatpp::String indentDeltaStr = ss.toString();
ss.setCurrentPosition(0);
if(firstLineIndent) {
ss << indentStr0;
}
switch (m_type) {
case Type::UNDEFINED: {
ss << "undefined";
break;
}
case Type::NULL_VALUE: {
ss << "null";
break;
}
case Type::INTEGER: {
ss << getInteger() << " (integer)";
break;
}
case Type::FLOAT: {
ss << getFloat() << " (float)";
break;
}
case Type::BOOL: {
ss << getValue<bool>() << " (bool)";
break;
}
case Type::INT_8: {
ss << getValue<v_int8>() << " (int_8)";
break;
}
case Type::UINT_8: {
ss << getValue<v_uint8>() << " (uint_8)";
break;
}
case Type::INT_16: {
ss << getValue<v_int16>() << " (int_16)";
break;
}
case Type::UINT_16: {
ss << getValue<v_uint16>() << " (uint_16)";
break;
}
case Type::INT_32: {
ss << getValue<v_int32 >() << " (int_32)";
break;
}
case Type::UINT_32: {
ss << getValue<v_uint32>() << " (uint_32)";
break;
}
case Type::INT_64: {
ss << getValue<v_int64>() << " (int_64)";
break;
}
case Type::UINT_64: {
ss << getValue<v_uint64>() << " (uint_64)";
break;
}
case Type::FLOAT_32: {
ss << getValue<v_float32>() << " (float_32)";
break;
}
case Type::FLOAT_64: {
ss << getValue<v_float64>() << " (float_64)";
break;
}
case Type::STRING: {
ss << "'" << getString() << "'";
break;
}
case Type::VECTOR: {
ss << "[\n";
auto& vector = getVector();
for(auto& v : vector) {
ss << v.debugPrint(indent0 + indentDelta, indentDelta) << "\n";
}
ss << indentStr0 << "]";
break;
}
case Type::MAP: {
ss << "{\n";
auto& map = getMap();
for(v_uint32 i = 0; i < map.size(); i ++) {
const auto& node = map[i];
ss << indentStr0 << indentDeltaStr << node.first << ": " << node.second.get().debugPrint(indent0 + indentDelta, indentDelta, false) << "\n";
}
ss << indentStr0 << "}";
break;
}
default:
break;
}
return ss.toString();
}
}}

View File

@ -129,30 +129,38 @@ public:
template <typename T, typename enabled = typename NodePrimitiveType<T>::value_type>
operator T () const {
auto size = primitiveDataSize();
if(size < 0) {
throw std::runtime_error("[oatpp::data::Tree::operator T ()]: Value NOT a Primitive type.");
switch (m_type) {
case Type::UNDEFINED:
case Type::NULL_VALUE: break;
case Type::INTEGER: return static_cast<T>(getInteger());
case Type::FLOAT: return static_cast<T>(getFloat());
case Type::BOOL: return static_cast<T>(getValue<bool>());
case Type::INT_8: return static_cast<T>(getValue<v_int8>());
case Type::UINT_8: return static_cast<T>(getValue<v_uint8>());
case Type::INT_16: return static_cast<T>(getValue<v_int16>());
case Type::UINT_16: return static_cast<T>(getValue<v_uint16>());
case Type::INT_32: return static_cast<T>(getValue<v_int32>());
case Type::UINT_32: return static_cast<T>(getValue<v_uint32>());
case Type::INT_64: return static_cast<T>(getValue<v_int64>());
case Type::UINT_64: return static_cast<T>(getValue<v_uint64>());
case Type::FLOAT_32: return static_cast<T>(getValue<v_float32>());
case Type::FLOAT_64: return static_cast<T>(getValue<v_float64>());
case Type::STRING:
case Type::VECTOR:
case Type::MAP:
default:
break;
}
if(isIntPrimitive()) {
LARGEST_TYPE value = 0;
std::memcpy (&value, &m_data, static_cast<size_t>(size));
return static_cast<T>(value);
}
if(isFloatPrimitive()) {
if(size == 4) {
v_float32 value;
std::memcpy (&value, &m_data, static_cast<size_t>(size));
return static_cast<T>(value);
} else if(size == 8) {
v_float64 value;
std::memcpy (&value, &m_data, static_cast<size_t>(size));
return static_cast<T>(value);
}
}
throw std::runtime_error("[oatpp::data::Tree::operator T ()]: Unable to autocast.");
throw std::runtime_error("[oatpp::data::Tree::operator T ()]: Value is NOT a Primitive type.");
}
@ -217,6 +225,8 @@ public:
std::vector<Tree>& getVector();
Map& getMap();
oatpp::String debugPrint(v_uint32 indent0 = 0, v_uint32 indentDelta = 2, bool firstLineIndent = true) const;
};
//////////////////////////////////////////////////////

View File

@ -0,0 +1,385 @@
/***************************************************************************
*
* 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 "TreeToObjectMapper.hpp"
#include "oatpp/data/stream/BufferStream.hpp"
#include "oatpp/utils/Conversion.hpp"
namespace oatpp { namespace data {
oatpp::String TreeToObjectMapper::MappingState::errorStacktrace() const {
stream::BufferOutputStream ss;
for(auto& s : errorStack) {
ss << s << "\n";
}
return ss.toString();
}
TreeToObjectMapper::TreeToObjectMapper() {
m_methods.resize(static_cast<size_t>(data::mapping::type::ClassId::getClassCount()), nullptr);
setMapperMethod(data::mapping::type::__class::String::CLASS_ID, &TreeToObjectMapper::mapString);
setMapperMethod(data::mapping::type::__class::Any::CLASS_ID, &TreeToObjectMapper::mapAny);
setMapperMethod(data::mapping::type::__class::Int8::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::Int8>);
setMapperMethod(data::mapping::type::__class::UInt8::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::UInt8>);
setMapperMethod(data::mapping::type::__class::Int16::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::Int16>);
setMapperMethod(data::mapping::type::__class::UInt16::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::UInt16>);
setMapperMethod(data::mapping::type::__class::Int32::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::Int32>);
setMapperMethod(data::mapping::type::__class::UInt32::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::UInt32>);
setMapperMethod(data::mapping::type::__class::Int64::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::Int64>);
setMapperMethod(data::mapping::type::__class::UInt64::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::UInt64>);
setMapperMethod(data::mapping::type::__class::Float32::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::Float32>);
setMapperMethod(data::mapping::type::__class::Float64::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::Float64>);
setMapperMethod(data::mapping::type::__class::Boolean::CLASS_ID, &TreeToObjectMapper::mapPrimitive<oatpp::Boolean>);
setMapperMethod(data::mapping::type::__class::AbstractObject::CLASS_ID, &TreeToObjectMapper::mapObject);
setMapperMethod(data::mapping::type::__class::AbstractEnum::CLASS_ID, &TreeToObjectMapper::mapEnum);
setMapperMethod(data::mapping::type::__class::AbstractVector::CLASS_ID, &TreeToObjectMapper::mapCollection);
setMapperMethod(data::mapping::type::__class::AbstractList::CLASS_ID, &TreeToObjectMapper::mapCollection);
setMapperMethod(data::mapping::type::__class::AbstractUnorderedSet::CLASS_ID, &TreeToObjectMapper::mapCollection);
setMapperMethod(data::mapping::type::__class::AbstractPairList::CLASS_ID, &TreeToObjectMapper::mapMap);
setMapperMethod(data::mapping::type::__class::AbstractUnorderedMap::CLASS_ID, &TreeToObjectMapper::mapMap);
}
void TreeToObjectMapper::setMapperMethod(const data::mapping::type::ClassId& classId, MapperMethod method) {
const auto id = static_cast<v_uint32>(classId.id);
if(id >= m_methods.size()) {
m_methods.resize(id + 1, nullptr);
}
m_methods[id] = method;
}
oatpp::Void TreeToObjectMapper::map(MappingState& state, const Type* const type) {
auto id = static_cast<v_uint32>(type->classId.id);
auto& method = m_methods[id];
if(method) {
return (*method)(this, state, type);
} else {
auto* interpretation = type->findInterpretation(state.config->enabledInterpretations);
if(interpretation) {
return interpretation->fromInterpretation(map(state, interpretation->getInterpretationType()));
}
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::map()]: "
"Error. No map method for type '" + std::string(type->classId.name) + "'");
return nullptr;
}
}
const Type* TreeToObjectMapper::guessType(const Tree& node) {
switch (node.getType()) {
case Tree::Type::UNDEFINED:
case Tree::Type::NULL_VALUE: return nullptr;
case Tree::Type::INTEGER: return Int64::Class::getType();
case Tree::Type::FLOAT: return Float64::Class::getType();
case Tree::Type::BOOL: return Boolean::Class::getType();
case Tree::Type::INT_8: return Int8::Class::getType();
case Tree::Type::UINT_8: return UInt8::Class::getType();
case Tree::Type::INT_16: return Int16::Class::getType();
case Tree::Type::UINT_16: return UInt16::Class::getType();
case Tree::Type::INT_32: return Int32::Class::getType();
case Tree::Type::UINT_32: return UInt32::Class::getType();
case Tree::Type::INT_64: return Int64::Class::getType();
case Tree::Type::UINT_64: return UInt64::Class::getType();
case Tree::Type::FLOAT_32: return Float32::Class::getType();
case Tree::Type::FLOAT_64: return Float64::Class::getType();
case Tree::Type::STRING: return String::Class::getType();
case Tree::Type::VECTOR: return Vector<oatpp::Any>::Class::getType();
case Tree::Type::MAP: return Fields<oatpp::Any>::Class::getType();
default: return nullptr;
}
}
oatpp::Void TreeToObjectMapper::mapString(TreeToObjectMapper* mapper, MappingState& state, const Type* const type) {
(void) mapper;
(void) type;
if(state.tree->isNull()){
return oatpp::Void(String::Class::getType());
}
if(state.tree->getType() == Tree::Type::STRING) {
return state.tree->getString();
}
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapString()]: Node is NOT a STRING");
return nullptr;
}
oatpp::Void TreeToObjectMapper::mapAny(TreeToObjectMapper* mapper, MappingState& state, const Type* const type) {
(void) type;
if(state.tree->isNull()){
return oatpp::Void(Any::Class::getType());
} else {
const Type* const fieldType = guessType(*state.tree);
if(fieldType != nullptr) {
auto fieldValue = mapper->map(state, fieldType);
auto anyHandle = std::make_shared<data::mapping::type::AnyHandle>(fieldValue.getPtr(), fieldValue.getValueType());
return oatpp::Void(anyHandle, Any::Class::getType());
}
}
return oatpp::Void(Any::Class::getType());
}
oatpp::Void TreeToObjectMapper::mapEnum(TreeToObjectMapper* mapper, MappingState& state, const Type* const type) {
auto polymorphicDispatcher = static_cast<const data::mapping::type::__class::AbstractEnum::PolymorphicDispatcher*>(
type->polymorphicDispatcher
);
data::mapping::type::EnumInterpreterError e = data::mapping::type::EnumInterpreterError::OK;
const auto& value = mapper->map(state, polymorphicDispatcher->getInterpretationType());
if(!state.errorStack.empty()) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapEnum()]");
return nullptr;
}
const auto& result = polymorphicDispatcher->fromInterpretation(value, e);
if(e == data::mapping::type::EnumInterpreterError::OK) {
return result;
}
switch(e) {
case data::mapping::type::EnumInterpreterError::CONSTRAINT_NOT_NULL:
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapEnum()]: Error. Enum constraint violated - 'NotNull'.");
break;
case data::mapping::type::EnumInterpreterError::OK:
case data::mapping::type::EnumInterpreterError::TYPE_MISMATCH_ENUM:
case data::mapping::type::EnumInterpreterError::TYPE_MISMATCH_ENUM_VALUE:
case data::mapping::type::EnumInterpreterError::ENTRY_NOT_FOUND:
default:
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapEnum()]: Error. Can't map Enum.");
}
return nullptr;
}
oatpp::Void TreeToObjectMapper::mapCollection(TreeToObjectMapper* mapper, MappingState& state, const Type* const type) {
if(state.tree->isNull()){
return oatpp::Void(type);
}
if(state.tree->getType() != Tree::Type::VECTOR) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapCollection()]: Node is NOT a VECTOR.");
return nullptr;
}
auto dispatcher = static_cast<const data::mapping::type::__class::Collection::PolymorphicDispatcher*>(type->polymorphicDispatcher);
auto collection = dispatcher->createObject();
auto itemType = dispatcher->getItemType();
const auto& vector = state.tree->getVector();
v_int64 index = 0;
for(const auto& node : vector) {
MappingState nestedState;
nestedState.tree = &node;
nestedState.config = state.config;
auto item = mapper->map(nestedState, itemType);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(state.errorStack.end(), nestedState.errorStack);
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapCollection()]: index=" + utils::Conversion::int64ToStr(index));
return nullptr;
}
dispatcher->addItem(collection, item);
index ++;
}
return collection;
}
oatpp::Void TreeToObjectMapper::mapMap(TreeToObjectMapper* mapper, MappingState& state, const Type* const type) {
if(state.tree->isNull()){
return oatpp::Void(type);
}
if(state.tree->getType() != Tree::Type::MAP) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapMap()]: Node is NOT a MAP.");
return nullptr;
}
auto dispatcher = static_cast<const data::mapping::type::__class::Map::PolymorphicDispatcher*>(type->polymorphicDispatcher);
auto map = dispatcher->createObject();
auto keyType = dispatcher->getKeyType();
if(keyType->classId != oatpp::String::Class::CLASS_ID){
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapMap()]: Invalid map key. Key should be String");
return nullptr;
}
auto valueType = dispatcher->getValueType();
const auto& treeMap = state.tree->getMap();
auto treeMapSize = treeMap.size();
for(v_uint64 i = 0; i < treeMapSize; i ++) {
const auto& node = treeMap[i];
MappingState nestedState;
nestedState.tree = &node.second.get();
nestedState.config = state.config;
auto item = mapper->map(nestedState, valueType);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(state.errorStack.end(), nestedState.errorStack);
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapMap()]: key='" + node.first + "'");
return nullptr;
}
dispatcher->addItem(map, node.first, item);
}
return map;
}
oatpp::Void TreeToObjectMapper::mapObject(TreeToObjectMapper* mapper, MappingState& state, const Type* const type) {
if(state.tree->isNull()){
return oatpp::Void(type);
}
if(state.tree->getType() != Tree::Type::MAP) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapObject()]: Node is NOT a MAP.");
return nullptr;
}
auto dispatcher = static_cast<const oatpp::data::mapping::type::__class::AbstractObject::PolymorphicDispatcher*>(type->polymorphicDispatcher);
auto object = dispatcher->createObject();
const auto& fieldsMap = dispatcher->getProperties()->getMap();
std::vector<std::pair<oatpp::BaseObject::Property*, const Tree*>> polymorphs;
const auto& treeMap = state.tree->getMap();
auto treeMapSize = treeMap.size();
for(v_uint64 i = 0; i < treeMapSize; i ++) {
const auto& node = treeMap[i];
auto fieldIterator = fieldsMap.find(node.first);
if(fieldIterator != fieldsMap.end()){
auto field = fieldIterator->second;
if(field->info.typeSelector && field->type == oatpp::Any::Class::getType()) {
polymorphs.emplace_back(field, &node.second.get()); // store polymorphs for later processing.
} else {
MappingState nestedState;
nestedState.tree = &node.second.get();
nestedState.config = state.config;
auto value = mapper->map(nestedState, field->type);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(state.errorStack.end(), nestedState.errorStack);
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapObject()]: field='" + node.first + "'");
return nullptr;
}
if(field->info.required && value == nullptr) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapObject()]: Error. "
+ oatpp::String(type->nameQualifier) + "::"
+ oatpp::String(field->name) + " is required!");
return nullptr;
}
field->set(static_cast<oatpp::BaseObject *>(object.get()), value);
}
} else if (!state.config->allowUnknownFields) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapObject()]: Error. Unknown field '" + node.first + "'");
return nullptr;
}
}
for(auto& p : polymorphs) {
auto selectedType = p.first->info.typeSelector->selectType(static_cast<oatpp::BaseObject *>(object.get()));
MappingState nestedState;
nestedState.tree = p.second;
nestedState.config = state.config;
auto value = mapper->map(nestedState, selectedType);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(state.errorStack.end(), nestedState.errorStack);
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapObject()]: field='" + oatpp::String(p.first->name) + "'");
return nullptr;
}
if(p.first->info.required && value == nullptr) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapObject()]: Error. "
+ oatpp::String(type->nameQualifier) + "::"
+ oatpp::String(p.first->name) + " is required!");
return nullptr;
}
oatpp::Any any(value);
p.first->set(static_cast<oatpp::BaseObject *>(object.get()), oatpp::Void(any.getPtr(), p.first->type));
}
return object;
}
}}

View File

@ -0,0 +1,95 @@
/***************************************************************************
*
* 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_TreeToObjectMapper_hpp
#define oatpp_data_TreeToObjectMapper_hpp
#include "./Tree.hpp"
namespace oatpp { namespace data {
class TreeToObjectMapper : public base::Countable {
public:
struct Config {
bool allowUnknownFields = true;
std::vector<std::string> enabledInterpretations = {};
};
public:
struct MappingState {
Config* config;
const Tree* tree;
std::list<oatpp::String> errorStack;
oatpp::String errorStacktrace() const;
};
public:
typedef oatpp::Void (*MapperMethod)(TreeToObjectMapper*, MappingState&, const Type* const);
public:
template<class T>
static oatpp::Void mapPrimitive(TreeToObjectMapper* mapper, MappingState& state, const Type* const type){
(void) mapper;
(void) type;
if(state.tree->isNull()) {
return oatpp::Void(T::Class::getType());
}
if(!state.tree->isPrimitive()) {
state.errorStack.emplace_back("[oatpp::data::TreeToObjectMapper::mapPrimitive()]: Value is NOT a Primitive type");
return nullptr;
}
return T(state.tree->operator typename T::UnderlyingType());
}
static const Type* guessType(const Tree& node);
static oatpp::Void mapString(TreeToObjectMapper* mapper, MappingState& state, const Type* const type);
static oatpp::Void mapAny(TreeToObjectMapper* mapper, MappingState& state, const Type* const type);
static oatpp::Void mapEnum(TreeToObjectMapper* mapper, MappingState& state, const Type* const type);
static oatpp::Void mapCollection(TreeToObjectMapper* mapper, MappingState& state, const Type* type);
static oatpp::Void mapMap(TreeToObjectMapper* mapper, MappingState& state, const Type* const type);
static oatpp::Void mapObject(TreeToObjectMapper* mapper, MappingState& state, const Type* const type);
private:
std::vector<MapperMethod> m_methods;
public:
TreeToObjectMapper();
void setMapperMethod(const data::mapping::type::ClassId& classId, MapperMethod method);
oatpp::Void map(MappingState& state, const Type* const type);
};
}}
#endif //oatpp_data_TreeToObjectMapper_hpp

View File

@ -48,6 +48,8 @@ add_executable(oatppAllTests
oatpp/data/stream/BufferStreamTest.hpp
oatpp/data/TreeTest.cpp
oatpp/data/TreeTest.hpp
oatpp/data/TreeToObjectMapperTest.cpp
oatpp/data/TreeToObjectMapperTest.hpp
oatpp/encoding/Base64Test.cpp
oatpp/encoding/Base64Test.hpp
oatpp/encoding/UnicodeTest.cpp

View File

@ -53,7 +53,10 @@
#include "oatpp/data/resource/InMemoryDataTest.hpp"
#include "oatpp/data/stream/BufferStreamTest.hpp"
#include "oatpp/data/TreeTest.hpp"
#include "oatpp/data/TreeToObjectMapperTest.hpp"
#include "oatpp/data/share/LazyStringMapTest.hpp"
#include "oatpp/data/share/StringTemplateTest.hpp"
#include "oatpp/data/share/MemoryLabelTest.hpp"
@ -104,7 +107,8 @@ void runTests() {
OATPP_RUN_TEST(oatpp::data::stream::BufferStreamTest);
*/
OATPP_RUN_TEST(oatpp::data::TreeTest);
//OATPP_RUN_TEST(oatpp::data::TreeTest);
OATPP_RUN_TEST(oatpp::data::TreeToObjectMapperTest);
/*
OATPP_RUN_TEST(oatpp::data::mapping::type::ObjectWrapperTest);

View File

@ -0,0 +1,133 @@
/***************************************************************************
*
* 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 "TreeToObjectMapperTest.hpp"
#include "oatpp/json/ObjectMapper.hpp"
#include "oatpp/data/ObjectToTreeMapper.hpp"
#include "oatpp/data/TreeToObjectMapper.hpp"
#include "oatpp/macro/codegen.hpp"
#include <iostream>
namespace oatpp { namespace data {
namespace {
#include OATPP_CODEGEN_BEGIN(DTO)
class TestDto1 : public oatpp::DTO {
DTO_INIT(TestDto1, DTO)
DTO_FIELD(String, str);
DTO_FIELD(Int8, i8);
DTO_FIELD(UInt8, ui8);
DTO_FIELD(Int16, i16);
DTO_FIELD(UInt16, ui16);
DTO_FIELD(Int32, i32);
DTO_FIELD(UInt32, ui32);
DTO_FIELD(Int64, i64);
DTO_FIELD(UInt64, ui64);
DTO_FIELD(Vector<oatpp::Object<TestDto1>>, vector);
DTO_FIELD(Fields<oatpp::Object<TestDto1>>, map);
};
#include OATPP_CODEGEN_END(DTO)
}
void TreeToObjectMapperTest::onRun() {
json::ObjectMapper jsonMapper;
jsonMapper.getSerializer()->getConfig()->useBeautifier = true;
jsonMapper.getSerializer()->getConfig()->includeNullFields = false;
TreeToObjectMapper mapper;
TreeToObjectMapper::Config config;
ObjectToTreeMapper reverseMapper;
ObjectToTreeMapper::Config reverseConfig;
{
Tree tree;
tree["str"] = "Hello World!";
tree["i8"] = -8;
tree["ui8"] = 8;
tree["i16"] = -16;
tree["ui16"] = 16;
tree["i32"] = -32;
tree["ui32"] = 32;
tree["i64"] = -64;
tree["ui64"] = 64;
tree["vector"].setVector(3);
tree["vector"][0]["str"] = "nested_1 (in vector)";
tree["vector"][1]["str"] = "nested_2 (in vector)";
tree["vector"][2]["str"] = "nested_3 (in vector)";
tree["map"]["nested_1"]["i32"] = 1;
tree["map"]["nested_2"]["i32"] = 2;
tree["map"]["nested_3"]["i32"] = 3;
TreeToObjectMapper::MappingState state;
state.config = &config;
state.tree = &tree;
const auto& obj = mapper.map(state,oatpp::Object<TestDto1>::Class::getType());
if(state.errorStack.empty()) {
auto json = jsonMapper.writeToString(obj);
std::cout << *json << std::endl;
} else {
auto err = state.errorStacktrace();
std::cout << *err << std::endl;
}
Tree clonedTree;
ObjectToTreeMapper::MappingState reverseState;
reverseState.config = &reverseConfig;
reverseState.tree = &clonedTree;
reverseMapper.map(reverseState, obj);
auto debugStr = clonedTree.debugPrint();
std::cout << *debugStr << 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_json_TreeToObjectMapperTest_hpp
#define oatpp_json_TreeToObjectMapperTest_hpp
#include "oatpp-test/UnitTest.hpp"
namespace oatpp { namespace data {
class TreeToObjectMapperTest : public oatpp::test::UnitTest {
public:
TreeToObjectMapperTest() : UnitTest("TEST[oatpp::data::TreeToObjectMapperTest]") {}
void onRun() override;
};
}}
#endif /* oatpp_json_TreeToObjectMapperTest_hpp */