2
0
mirror of https://github.com/oatpp/oatpp.git synced 2025-04-12 18:50:22 +08:00

Tree: better mapping to/from object

This commit is contained in:
Leonid Stryzhevskyi 2024-05-06 00:23:48 +03:00
parent a29c664581
commit b9d78cce64
8 changed files with 206 additions and 50 deletions

@ -161,7 +161,7 @@ void ObjectToTreeMapper::mapCollection(const ObjectToTreeMapper* mapper, State&
auto iterator = dispatcher->beginIteration(polymorph);
state.tree->setVector(0);
auto& vector = state.tree->getVector();
TreeChildrenOperator childrenOperator(*state.tree);
v_int64 index = 0;
while (!iterator->finished()) {
@ -170,10 +170,8 @@ void ObjectToTreeMapper::mapCollection(const ObjectToTreeMapper* mapper, State&
if(value || state.config->includeNullFields || state.config->alwaysIncludeNullCollectionElements) {
vector.emplace_back();
State nestedState;
nestedState.tree = &vector[vector.size() - 1];
nestedState.tree = childrenOperator.putItem({});
nestedState.config = state.config;
mapper->map(nestedState, value);
@ -212,8 +210,15 @@ void ObjectToTreeMapper::mapMap(const ObjectToTreeMapper* mapper, State& state,
auto iterator = dispatcher->beginIteration(polymorph);
state.tree->setMap({});
auto& map = state.tree->getMap();
if(polymorph.getValueType()->classId == data::type::__class::AbstractUnorderedMap::CLASS_ID) {
state.tree->setMap({});
} else if(polymorph.getValueType()->classId == data::type::__class::AbstractPairList::CLASS_ID) {
state.tree->setPairs({});
} else {
state.errorStack.push("[oatpp::data::mapping::ObjectToTreeMapper::mapMap()]: Invalid polymorph type");
}
TreeChildrenOperator childrenOperator(*state.tree);
while (!iterator->finished()) {
const auto& value = iterator->getValue();
@ -223,7 +228,7 @@ void ObjectToTreeMapper::mapMap(const ObjectToTreeMapper* mapper, State& state,
const auto& key = oatpp::String(std::static_pointer_cast<std::string>(untypedKey.getPtr()));
State nestedState;
nestedState.tree = &map[key];
nestedState.tree = childrenOperator.putPair(key, {});
nestedState.config = state.config;
mapper->map(nestedState, value);
@ -255,7 +260,7 @@ void ObjectToTreeMapper::mapObject(const ObjectToTreeMapper* mapper, State& stat
auto object = static_cast<oatpp::BaseObject*>(polymorph.get());
state.tree->setMap({});
auto& map = state.tree->getMap();
TreeChildrenOperator childrenOperator(*state.tree);
for (auto const& field : fields) {
@ -277,7 +282,7 @@ void ObjectToTreeMapper::mapObject(const ObjectToTreeMapper* mapper, State& stat
if (value || state.config->includeNullFields || (field->info.required && state.config->alwaysIncludeRequired)) {
State nestedState;
nestedState.tree = &map[oatpp::String(field->name)];
nestedState.tree = childrenOperator.putPair(oatpp::String(field->name), {});
nestedState.config = state.config;
mapper->map(nestedState, value);

@ -784,33 +784,23 @@ TreeChildrenOperator::TreeChildrenOperator(Tree& tree)
: TreeChildrenOperator(const_cast<const Tree&>(tree))
{
m_const = false;
if(tree.getType() == Tree::Type::VECTOR) {
m_vector = std::addressof(tree.getVector());
} else if(tree.getType() == Tree::Type::MAP) {
m_map = std::addressof(tree.getMap());
} else if(tree.getType() == Tree::Type::PAIRS) {
m_pairs = std::addressof(tree.getPairs());
}
}
TreeChildrenOperator::TreeChildrenOperator(const Tree& tree)
: m_vector(nullptr)
, m_map(nullptr)
, m_pairs(nullptr)
, c_vector(nullptr)
, c_map(nullptr)
, c_pairs(nullptr)
{
m_const = true;
if(tree.getType() == Tree::Type::VECTOR) {
m_type = VECTOR;
c_vector = std::addressof(tree.getVector());
m_vector = std::addressof(tree.getVector());
} else if(tree.getType() == Tree::Type::MAP) {
m_type = MAP;
c_map = std::addressof(tree.getMap());
m_map = std::addressof(tree.getMap());
} else if(tree.getType() == Tree::Type::PAIRS) {
m_type = PAIRS;
c_pairs = std::addressof(tree.getPairs());
m_pairs = std::addressof(tree.getPairs());
} else {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::TreeChildrenOperator()]: Node type is NOT suppoerted");
}
@ -823,11 +813,11 @@ std::pair<type::String, Tree*> TreeChildrenOperator::getPair(v_uint64 index) {
switch (m_type) {
case VECTOR: break;
case MAP: {
const auto& p = (*m_map)[index];
const auto& p = (*const_cast<TreeMap*>(m_map))[index];
return {p.first, std::addressof(p.second.get())};
}
case PAIRS: {
auto& p = m_pairs->at(index);
auto& p = const_cast<std::vector<std::pair<type::String, Tree>>*>(m_pairs)->at(index);
return {p.first, &p.second};
}
default:
@ -840,11 +830,11 @@ std::pair<type::String, const Tree*> TreeChildrenOperator::getPair(v_uint64 inde
switch (m_type) {
case VECTOR: break;
case MAP: {
const auto& p = (*c_map)[index];
const auto& p = (*m_map)[index];
return {p.first, std::addressof(p.second.get())};
}
case PAIRS: {
auto& p = (*c_pairs)[index];
auto& p = (*m_pairs)[index];
return {p.first, &p.second};
}
default:
@ -858,9 +848,9 @@ Tree* TreeChildrenOperator::getItem(v_uint64 index) {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getItem()]: Can't operate on CONST tree node");
}
switch (m_type) {
case VECTOR: return std::addressof(m_vector->at(index));
case MAP: return std::addressof((*m_map)[index].second.get());
case PAIRS: return &m_pairs->at(index).second;
case VECTOR: return std::addressof(const_cast<std::vector<Tree>*>(m_vector)->at(index));
case MAP: return std::addressof((*const_cast<TreeMap*>(m_map))[index].second.get());
case PAIRS: return &const_cast<std::vector<std::pair<type::String, Tree>>*>(m_pairs)->at(index).second;
default:
break;
}
@ -869,20 +859,102 @@ Tree* TreeChildrenOperator::getItem(v_uint64 index) {
const Tree* TreeChildrenOperator::getItem(v_uint64 index) const {
switch (m_type) {
case VECTOR: return std::addressof(c_vector->at(index));
case MAP: return std::addressof((*c_map)[index].second.get());
case PAIRS: return &c_pairs->at(index).second;
case VECTOR: return std::addressof(m_vector->at(index));
case MAP: return std::addressof((*m_map)[index].second.get());
case PAIRS: return &m_pairs->at(index).second;
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getItem()]: Invalid operator type");
}
Tree* TreeChildrenOperator::putPair(const type::String& key, const Tree& tree) {
if(m_const) {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putPair()]: Can't operate on CONST tree node");
}
switch (m_type) {
case VECTOR: break;
case MAP: {
auto& node = (*const_cast<TreeMap*>(m_map))[key];
node = tree;
return std::addressof(node);
}
case PAIRS: {
auto& pairs = *const_cast<std::vector<std::pair<type::String, Tree>>*>(m_pairs);
pairs.emplace_back(key, tree);
auto& p = pairs.at(pairs.size() - 1);
return std::addressof(p.second);
}
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putPair()]: Node type doesn't support pairs");
}
Tree* TreeChildrenOperator::putPair(const type::String& key, Tree&& tree) {
if(m_const) {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putPair()]: Can't operate on CONST tree node");
}
switch (m_type) {
case VECTOR: break;
case MAP: {
auto& node = (*const_cast<TreeMap*>(m_map))[key];
node = std::forward<Tree>(tree);
return std::addressof(node);
}
case PAIRS: {
auto& pairs = *const_cast<std::vector<std::pair<type::String, Tree>>*>(m_pairs);
pairs.emplace_back(key, std::forward<Tree>(tree));
auto& p = pairs.at(pairs.size() - 1);
return std::addressof(p.second);
}
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putPair()]: Node type doesn't support pairs");
}
Tree* TreeChildrenOperator::putItem(const Tree& tree) {
if(m_const) {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putItem()]: Can't operate on CONST tree node");
}
switch (m_type) {
case VECTOR: {
auto& vector = *const_cast<std::vector<Tree>*>(m_vector);
vector.push_back(tree);
return std::addressof(vector.at(vector.size() - 1));
}
case MAP:
case PAIRS:
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putItem()]: Invalid iterator type");
}
Tree* TreeChildrenOperator::putItem(Tree&& tree) {
if(m_const) {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putItem()]: Can't operate on CONST tree node");
}
switch (m_type) {
case VECTOR: {
auto& vector = *const_cast<std::vector<Tree>*>(m_vector);
vector.emplace_back(std::forward<Tree>(tree));
return std::addressof(vector.at(vector.size() - 1));
}
case MAP:
case PAIRS:
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::putItem()]: Invalid iterator type");
}
v_uint64 TreeChildrenOperator::size() const {
switch (m_type) {
case VECTOR: return c_vector->size();
case MAP: return c_map->size();
case PAIRS: return c_pairs->size();
case VECTOR: return m_vector->size();
case MAP: return m_map->size();
case PAIRS: return m_pairs->size();
default:
break;
}

@ -268,20 +268,16 @@ private:
PAIRS
};
private:
std::vector<Tree>* m_vector;
TreeMap* m_map;
std::vector<std::pair<type::String, Tree>>* m_pairs;
private:
const std::vector<Tree>* c_vector;
const TreeMap* c_map;
const std::vector<std::pair<type::String, Tree>>* c_pairs;
const std::vector<Tree>* m_vector;
const TreeMap* m_map;
const std::vector<std::pair<type::String, Tree>>* m_pairs;
private:
bool m_const;
IteratorType m_type;
public:
TreeChildrenOperator(Tree& tree);
TreeChildrenOperator(const Tree& tree);
explicit TreeChildrenOperator(Tree& tree);
explicit TreeChildrenOperator(const Tree& tree);
std::pair<type::String, Tree*> getPair(v_uint64 index);
std::pair<type::String, const Tree*> getPair(v_uint64 index) const;
@ -289,6 +285,12 @@ public:
Tree* getItem(v_uint64 index);
const Tree* getItem(v_uint64 index) const;
Tree* putPair(const type::String& key, const Tree& tree);
Tree* putPair(const type::String& key, Tree&& tree);
Tree* putItem(const Tree& tree);
Tree* putItem(Tree&& tree);
v_uint64 size() const;
};

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

@ -55,6 +55,7 @@
#include "oatpp/data/stream/BufferStreamTest.hpp"
#include "oatpp/data/mapping/TreeTest.hpp"
#include "oatpp/data/mapping/ObjectToTreeMapperTest.hpp"
#include "oatpp/data/mapping/TreeToObjectMapperTest.hpp"
#include "oatpp/data/share/LazyStringMapTest.hpp"
@ -115,9 +116,10 @@ void runTests() {
// OATPP_RUN_TEST(oatpp::data::stream::BufferStreamTest);
//
OATPP_RUN_TEST(oatpp::data::mapping::TreeTest);
//OATPP_RUN_TEST(oatpp::data::mapping::TreeTest);
OATPP_RUN_TEST(oatpp::data::mapping::TreeToObjectMapperTest);
OATPP_RUN_TEST(oatpp::data::mapping::ObjectToTreeMapperTest);
//OATPP_RUN_TEST(oatpp::data::mapping::TreeToObjectMapperTest);
//
//

@ -0,0 +1,33 @@
/***************************************************************************
*
* 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 "ObjectToTreeMapperTest.hpp"
namespace oatpp { namespace data { namespace mapping {
void ObjectToTreeMapperTest::onRun() {
}
}}}

@ -0,0 +1,40 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_data_mapping_ObjectToTreeMapperTest_hpp
#define oatpp_data_mapping_ObjectToTreeMapperTest_hpp
#include "oatpp-test/UnitTest.hpp"
namespace oatpp { namespace data { namespace mapping {
class ObjectToTreeMapperTest : public oatpp::test::UnitTest {
public:
ObjectToTreeMapperTest() : UnitTest("TEST[oatpp::data::mapping::ObjectToTreeMapperTest]") {}
void onRun() override;
};
}}}
#endif /* oatpp_data_mapping_ObjectToTreeMapperTest_hpp */

@ -330,7 +330,7 @@ void DTOMapperTest::onRun(){
oatpp::String result;
try {
result = mapper.writeToString(test2);
} catch(std::runtime_error& e) {
} catch(std::runtime_error&) {
OATPP_LOGV(TAG, "Test2::field_string is required!")
}
OATPP_ASSERT(result == nullptr)
@ -340,7 +340,7 @@ void DTOMapperTest::onRun(){
auto test3 = Test3::createShared();
try {
auto result = mapper.writeToString(test3);
} catch(std::runtime_error& e) {
} catch(std::runtime_error&) {
OATPP_ASSERT(false)
}
}
@ -352,7 +352,7 @@ void DTOMapperTest::onRun(){
oatpp::String result;
try {
result = mapper.writeToString(test4);
} catch(std::runtime_error& e) {
} catch(std::runtime_error&) {
OATPP_LOGV(TAG, "TestChild1::name is required!")
}
OATPP_ASSERT(result == nullptr)
@ -364,7 +364,7 @@ void DTOMapperTest::onRun(){
test5->child = TestChild2::createShared();
try {
auto result = mapper.writeToString(test5);
} catch(std::runtime_error& e) {
} catch(std::runtime_error&) {
OATPP_ASSERT(false)
}
}