mirror of
https://gitee.com/zyjblog/oatpp.git
synced 2025-01-03 05:22:24 +08:00
Tree: introduce ObjectToTreeMapper and TreeToObjectMapper. WIP
This commit is contained in:
parent
4a7795c221
commit
1d64355eef
@ -62,8 +62,12 @@ add_library(oatpp
|
|||||||
oatpp/concurrency/Utils.hpp
|
oatpp/concurrency/Utils.hpp
|
||||||
oatpp/data/Bundle.cpp
|
oatpp/data/Bundle.cpp
|
||||||
oatpp/data/Bundle.hpp
|
oatpp/data/Bundle.hpp
|
||||||
|
oatpp/data/ObjectToTreeMapper.cpp
|
||||||
|
oatpp/data/ObjectToTreeMapper.hpp
|
||||||
oatpp/data/Tree.cpp
|
oatpp/data/Tree.cpp
|
||||||
oatpp/data/Tree.hpp
|
oatpp/data/Tree.hpp
|
||||||
|
oatpp/data/TreeToObjectMapper.cpp
|
||||||
|
oatpp/data/TreeToObjectMapper.hpp
|
||||||
oatpp/data/buffer/FIFOBuffer.cpp
|
oatpp/data/buffer/FIFOBuffer.cpp
|
||||||
oatpp/data/buffer/FIFOBuffer.hpp
|
oatpp/data/buffer/FIFOBuffer.hpp
|
||||||
oatpp/data/buffer/IOBuffer.cpp
|
oatpp/data/buffer/IOBuffer.cpp
|
||||||
|
301
src/oatpp/data/ObjectToTreeMapper.cpp
Normal file
301
src/oatpp/data/ObjectToTreeMapper.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
91
src/oatpp/data/ObjectToTreeMapper.hpp
Normal file
91
src/oatpp/data/ObjectToTreeMapper.hpp
Normal 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
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include "Tree.hpp"
|
#include "Tree.hpp"
|
||||||
|
|
||||||
|
#include "oatpp/data/stream/BufferStream.hpp"
|
||||||
|
|
||||||
namespace oatpp { namespace data {
|
namespace oatpp { namespace data {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -495,4 +497,122 @@ Tree::Map& Tree::getMap() {
|
|||||||
return *data;
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
@ -129,30 +129,38 @@ public:
|
|||||||
template <typename T, typename enabled = typename NodePrimitiveType<T>::value_type>
|
template <typename T, typename enabled = typename NodePrimitiveType<T>::value_type>
|
||||||
operator T () const {
|
operator T () const {
|
||||||
|
|
||||||
auto size = primitiveDataSize();
|
switch (m_type) {
|
||||||
if(size < 0) {
|
|
||||||
throw std::runtime_error("[oatpp::data::Tree::operator T ()]: Value NOT a Primitive 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()) {
|
throw std::runtime_error("[oatpp::data::Tree::operator T ()]: Value is NOT a Primitive type.");
|
||||||
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.");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +225,8 @@ public:
|
|||||||
std::vector<Tree>& getVector();
|
std::vector<Tree>& getVector();
|
||||||
Map& getMap();
|
Map& getMap();
|
||||||
|
|
||||||
|
oatpp::String debugPrint(v_uint32 indent0 = 0, v_uint32 indentDelta = 2, bool firstLineIndent = true) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
|
385
src/oatpp/data/TreeToObjectMapper.cpp
Normal file
385
src/oatpp/data/TreeToObjectMapper.cpp
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
95
src/oatpp/data/TreeToObjectMapper.hpp
Normal file
95
src/oatpp/data/TreeToObjectMapper.hpp
Normal 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
|
@ -48,6 +48,8 @@ add_executable(oatppAllTests
|
|||||||
oatpp/data/stream/BufferStreamTest.hpp
|
oatpp/data/stream/BufferStreamTest.hpp
|
||||||
oatpp/data/TreeTest.cpp
|
oatpp/data/TreeTest.cpp
|
||||||
oatpp/data/TreeTest.hpp
|
oatpp/data/TreeTest.hpp
|
||||||
|
oatpp/data/TreeToObjectMapperTest.cpp
|
||||||
|
oatpp/data/TreeToObjectMapperTest.hpp
|
||||||
oatpp/encoding/Base64Test.cpp
|
oatpp/encoding/Base64Test.cpp
|
||||||
oatpp/encoding/Base64Test.hpp
|
oatpp/encoding/Base64Test.hpp
|
||||||
oatpp/encoding/UnicodeTest.cpp
|
oatpp/encoding/UnicodeTest.cpp
|
||||||
|
@ -53,7 +53,10 @@
|
|||||||
#include "oatpp/data/resource/InMemoryDataTest.hpp"
|
#include "oatpp/data/resource/InMemoryDataTest.hpp"
|
||||||
|
|
||||||
#include "oatpp/data/stream/BufferStreamTest.hpp"
|
#include "oatpp/data/stream/BufferStreamTest.hpp"
|
||||||
|
|
||||||
#include "oatpp/data/TreeTest.hpp"
|
#include "oatpp/data/TreeTest.hpp"
|
||||||
|
#include "oatpp/data/TreeToObjectMapperTest.hpp"
|
||||||
|
|
||||||
#include "oatpp/data/share/LazyStringMapTest.hpp"
|
#include "oatpp/data/share/LazyStringMapTest.hpp"
|
||||||
#include "oatpp/data/share/StringTemplateTest.hpp"
|
#include "oatpp/data/share/StringTemplateTest.hpp"
|
||||||
#include "oatpp/data/share/MemoryLabelTest.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::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);
|
OATPP_RUN_TEST(oatpp::data::mapping::type::ObjectWrapperTest);
|
||||||
|
133
test/oatpp/data/TreeToObjectMapperTest.cpp
Normal file
133
test/oatpp/data/TreeToObjectMapperTest.cpp
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
40
test/oatpp/data/TreeToObjectMapperTest.hpp
Normal file
40
test/oatpp/data/TreeToObjectMapperTest.hpp
Normal 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 */
|
Loading…
Reference in New Issue
Block a user