Merge pull request #861 from linhaojun857/issue_676

Fix serialize object and deserialize json when not include required field
This commit is contained in:
Leonid Stryzhevskyi 2023-10-08 04:43:31 +03:00 committed by GitHub
commit d2c06b65af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 344 additions and 149 deletions

View File

@ -447,7 +447,13 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser::
skipValue(caret);
polymorphs.emplace_back(field, label.toString()); // store polymorphs for later processing.
} else {
field->set(static_cast<oatpp::BaseObject *>(object.get()), deserializer->deserialize(caret, field->type));
auto value = deserializer->deserialize(caret, field->type);
if(field->info.required && value == nullptr) {
throw std::runtime_error("[oatpp::parser::json::mapping::Deserializer::deserialize()]: "
"Error. " + std::string(type->nameQualifier) + "::"
+ std::string(field->name) + " is required!");
}
field->set(static_cast<oatpp::BaseObject *>(object.get()), value);
}
} else if (deserializer->getConfig()->allowUnknownFields) {
@ -479,6 +485,11 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser::
parser::Caret polyCaret(p.second);
auto selectedType = p.first->info.typeSelector->selectType(static_cast<oatpp::BaseObject *>(object.get()));
auto value = deserializer->deserialize(polyCaret, selectedType);
if(p.first->info.required && value == nullptr) {
throw std::runtime_error("[oatpp::parser::json::mapping::Deserializer::deserialize()]: "
"Error. " + std::string(type->nameQualifier) + "::"
+ std::string(p.first->name) + " is required!");
}
oatpp::Any any(value);
p.first->set(static_cast<oatpp::BaseObject *>(object.get()), oatpp::Void(any.getPtr(), p.first->type));
}

View File

@ -226,8 +226,9 @@ void Serializer::serializeObject(Serializer* serializer,
stream->writeCharSimple('{');
bool first = true;
auto type = polymorph.getValueType();
auto dispatcher = static_cast<const oatpp::data::mapping::type::__class::AbstractObject::PolymorphicDispatcher*>(
polymorph.getValueType()->polymorphicDispatcher
type->polymorphicDispatcher
);
auto fields = dispatcher->getProperties()->getList();
auto object = static_cast<oatpp::BaseObject*>(polymorph.get());
@ -243,6 +244,12 @@ void Serializer::serializeObject(Serializer* serializer,
value = field->get(object);
}
if(field->info.required && value == nullptr) {
throw std::runtime_error("[oatpp::parser::json::mapping::Serializer::serialize()]: "
"Error. " + std::string(type->nameQualifier) + "::"
+ std::string(field->name) + " is required!");
}
if (value || config->includeNullFields || (field->info.required && config->alwaysIncludeRequired)) {
(first) ? first = false : stream->writeSimple(",", 1);
serializeString(stream, field->name, static_cast<v_buff_size>(std::strlen(field->name)), serializer->m_config->escapeFlags);

View File

@ -90,6 +90,57 @@ class Test : public oatpp::DTO {
};
class Test2 : public oatpp::DTO {
DTO_INIT(Test2, DTO)
DTO_FIELD_INFO(field_string) {
info->required = true;
}
DTO_FIELD(String, field_string);
};
class Test3 : public oatpp::DTO {
DTO_INIT(Test3, DTO)
DTO_FIELD(String, field_string);
};
class TestChild1 : public oatpp::DTO {
DTO_INIT(TestChild1, DTO)
DTO_FIELD_INFO(name) {
info->required = true;
}
DTO_FIELD(String, name);
};
class Test4 : public oatpp::DTO {
DTO_INIT(Test4, DTO)
DTO_FIELD(String, field_string);
DTO_FIELD(Object<TestChild1>, child);
};
class TestChild2 : public oatpp::DTO {
DTO_INIT(TestChild2, DTO)
DTO_FIELD(String, name);
};
class Test5 : public oatpp::DTO {
DTO_INIT(Test5, DTO)
DTO_FIELD(String, field_string);
DTO_FIELD(Object<TestChild2>, child);
};
class TestAny : public oatpp::DTO {
DTO_INIT(TestAny, DTO)
@ -116,161 +167,207 @@ void DTOMapperTest::onRun(){
auto mapper = oatpp::parser::json::mapping::ObjectMapper::createShared();
mapper->getSerializer()->getConfig()->useBeautifier = true;
auto test1 = Test::createShared();
test1->field_string = "string value";
test1->field_int32 = 32;
test1->field_int64 = 64;
test1->field_float32 = 0.32f;
test1->field_float64 = 0.64;
test1->field_boolean = true;
test1->obj1 = Test::createShared();
test1->obj1->field_string = "inner string";
test1->obj1->field_list_string->push_back("inner str_item_1");
test1->obj1->field_list_string->push_back("inner str_item_2");
test1->obj1->field_list_string->push_back("inner str_item_3");
test1->child1 = TestChild::createShared();
test1->child1->name = "child1_name";
test1->child1->secondName = "child1_second_name";
test1->field_list_string->push_back("str_item_1");
test1->field_list_string->push_back("str_item_2");
test1->field_list_string->push_back("str_item_3");
test1->field_list_int32->push_back(321);
test1->field_list_int32->push_back(322);
test1->field_list_int32->push_back(323);
test1->field_list_int64->push_back(641);
test1->field_list_int64->push_back(642);
test1->field_list_int64->push_back(643);
test1->field_list_float32->push_back(0.321f);
test1->field_list_float32->push_back(0.322f);
test1->field_list_float32->push_back(0.323f);
test1->field_list_float64->push_back(0.641);
test1->field_list_float64->push_back(0.642);
test1->field_list_float64->push_back(0.643);
test1->field_list_boolean->push_back(true);
test1->field_list_boolean->push_back(false);
test1->field_list_boolean->push_back(true);
test1->field_list_object->push_back(TestChild::createShared("child", "1"));
test1->field_list_object->push_back(TestChild::createShared("child", "2"));
test1->field_list_object->push_back(TestChild::createShared("child", "3"));
auto l1 = oatpp::List<oatpp::Object<TestChild>>::createShared();
auto l2 = oatpp::List<oatpp::Object<TestChild>>::createShared();
auto l3 = oatpp::List<oatpp::Object<TestChild>>::createShared();
l1->push_back(TestChild::createShared("list_1", "item_1"));
l1->push_back(TestChild::createShared("list_1", "item_2"));
l1->push_back(TestChild::createShared("list_1", "item_3"));
l2->push_back(TestChild::createShared("list_2", "item_1"));
l2->push_back(TestChild::createShared("list_2", "item_2"));
l2->push_back(TestChild::createShared("list_2", "item_3"));
l3->push_back(TestChild::createShared("list_3", "item_1"));
l3->push_back(TestChild::createShared("list_3", "item_2"));
l3->push_back(TestChild::createShared("list_3", "item_3"));
test1->field_list_list_object->push_back(l1);
test1->field_list_list_object->push_back(l2);
test1->field_list_list_object->push_back(l3);
test1->field_vector = {"vector_item1", "vector_item2", "vector_item3"};
test1->field_fields = {
{"key0", "pair_item0"},
{"key1", "pair_item1"},
{"key2", "pair_item2"},
{"key3", "pair_item3"},
{"key4", "pair_item4"},
{"key5", "pair_item5"},
{"key6", "pair_item6"},
{"key7", "pair_item7"},
{"key8", "pair_item8"},
{"key9", "pair_item9"},
{"key10", "pair_item10"},
{"key11", "pair_item11"}
};
test1->field_unordered_fields = {
{"key0", "map_item0"},
{"key1", "map_item1"},
{"key2", "map_item2"},
{"key3", "map_item3"},
{"key4", "map_item4"},
{"key5", "map_item5"},
{"key6", "map_item6"},
{"key7", "map_item7"},
{"key8", "map_item8"},
{"key9", "map_item9"},
{"key10", "map_item10"},
{"key11", "map_item11"}
};
auto result = mapper->writeToString(test1);
OATPP_LOGV(TAG, "json='%s'", result->c_str())
OATPP_LOGV(TAG, "...")
OATPP_LOGV(TAG, "...")
OATPP_LOGV(TAG, "...")
oatpp::parser::Caret caret(result);
auto obj1 = mapper->readFromCaret<oatpp::Object<Test>>(caret);
OATPP_ASSERT(obj1->field_string)
OATPP_ASSERT(obj1->field_string == test1->field_string)
OATPP_ASSERT(obj1->field_int32)
OATPP_ASSERT(obj1->field_int32 == test1->field_int32)
OATPP_ASSERT(obj1->field_int64)
OATPP_ASSERT(obj1->field_int64 == test1->field_int64)
OATPP_ASSERT(obj1->field_float32)
OATPP_ASSERT(fabsf(obj1->field_float32 - test1->field_float32) < std::numeric_limits<float>::epsilon())
OATPP_ASSERT(obj1->field_float64)
OATPP_ASSERT(fabs(obj1->field_float64 - test1->field_float64) < std::numeric_limits<double>::epsilon())
OATPP_ASSERT(obj1->field_boolean)
OATPP_ASSERT(obj1->field_boolean == test1->field_boolean)
{
auto c = obj1->field_vector;
OATPP_ASSERT(c[0] == "vector_item1")
OATPP_ASSERT(c[1] == "vector_item2")
OATPP_ASSERT(c[2] == "vector_item3")
auto test1 = Test::createShared();
test1->field_string = "string value";
test1->field_int32 = 32;
test1->field_int64 = 64;
test1->field_float32 = 0.32f;
test1->field_float64 = 0.64;
test1->field_boolean = true;
test1->obj1 = Test::createShared();
test1->obj1->field_string = "inner string";
test1->obj1->field_list_string->push_back("inner str_item_1");
test1->obj1->field_list_string->push_back("inner str_item_2");
test1->obj1->field_list_string->push_back("inner str_item_3");
test1->child1 = TestChild::createShared();
test1->child1->name = "child1_name";
test1->child1->secondName = "child1_second_name";
test1->field_list_string->push_back("str_item_1");
test1->field_list_string->push_back("str_item_2");
test1->field_list_string->push_back("str_item_3");
test1->field_list_int32->push_back(321);
test1->field_list_int32->push_back(322);
test1->field_list_int32->push_back(323);
test1->field_list_int64->push_back(641);
test1->field_list_int64->push_back(642);
test1->field_list_int64->push_back(643);
test1->field_list_float32->push_back(0.321f);
test1->field_list_float32->push_back(0.322f);
test1->field_list_float32->push_back(0.323f);
test1->field_list_float64->push_back(0.641);
test1->field_list_float64->push_back(0.642);
test1->field_list_float64->push_back(0.643);
test1->field_list_boolean->push_back(true);
test1->field_list_boolean->push_back(false);
test1->field_list_boolean->push_back(true);
test1->field_list_object->push_back(TestChild::createShared("child", "1"));
test1->field_list_object->push_back(TestChild::createShared("child", "2"));
test1->field_list_object->push_back(TestChild::createShared("child", "3"));
auto l1 = oatpp::List<oatpp::Object<TestChild>>::createShared();
auto l2 = oatpp::List<oatpp::Object<TestChild>>::createShared();
auto l3 = oatpp::List<oatpp::Object<TestChild>>::createShared();
l1->push_back(TestChild::createShared("list_1", "item_1"));
l1->push_back(TestChild::createShared("list_1", "item_2"));
l1->push_back(TestChild::createShared("list_1", "item_3"));
l2->push_back(TestChild::createShared("list_2", "item_1"));
l2->push_back(TestChild::createShared("list_2", "item_2"));
l2->push_back(TestChild::createShared("list_2", "item_3"));
l3->push_back(TestChild::createShared("list_3", "item_1"));
l3->push_back(TestChild::createShared("list_3", "item_2"));
l3->push_back(TestChild::createShared("list_3", "item_3"));
test1->field_list_list_object->push_back(l1);
test1->field_list_list_object->push_back(l2);
test1->field_list_list_object->push_back(l3);
test1->field_vector = {"vector_item1", "vector_item2", "vector_item3"};
test1->field_fields = {
{"key0", "pair_item0"},
{"key1", "pair_item1"},
{"key2", "pair_item2"},
{"key3", "pair_item3"},
{"key4", "pair_item4"},
{"key5", "pair_item5"},
{"key6", "pair_item6"},
{"key7", "pair_item7"},
{"key8", "pair_item8"},
{"key9", "pair_item9"},
{"key10", "pair_item10"},
{"key11", "pair_item11"}
};
test1->field_unordered_fields = {
{"key0", "map_item0"},
{"key1", "map_item1"},
{"key2", "map_item2"},
{"key3", "map_item3"},
{"key4", "map_item4"},
{"key5", "map_item5"},
{"key6", "map_item6"},
{"key7", "map_item7"},
{"key8", "map_item8"},
{"key9", "map_item9"},
{"key10", "map_item10"},
{"key11", "map_item11"}
};
auto result = mapper->writeToString(test1);
OATPP_LOGV(TAG, "json='%s'", result->c_str())
OATPP_LOGV(TAG, "...")
OATPP_LOGV(TAG, "...")
OATPP_LOGV(TAG, "...")
oatpp::parser::Caret caret(result);
auto obj1 = mapper->readFromCaret<oatpp::Object<Test>>(caret);
OATPP_ASSERT(obj1->field_string)
OATPP_ASSERT(obj1->field_string == test1->field_string)
OATPP_ASSERT(obj1->field_int32)
OATPP_ASSERT(obj1->field_int32 == test1->field_int32)
OATPP_ASSERT(obj1->field_int64)
OATPP_ASSERT(obj1->field_int64 == test1->field_int64)
OATPP_ASSERT(obj1->field_float32)
OATPP_ASSERT(fabsf(obj1->field_float32 - test1->field_float32) < std::numeric_limits<float>::epsilon())
OATPP_ASSERT(obj1->field_float64)
OATPP_ASSERT(fabs(obj1->field_float64 - test1->field_float64) < std::numeric_limits<double>::epsilon())
OATPP_ASSERT(obj1->field_boolean)
OATPP_ASSERT(obj1->field_boolean == test1->field_boolean)
{
auto c = obj1->field_vector;
OATPP_ASSERT(c[0] == "vector_item1")
OATPP_ASSERT(c[1] == "vector_item2")
OATPP_ASSERT(c[2] == "vector_item3")
}
{
auto c = obj1->field_fields;
v_int32 i = 0;
for(auto& pair : *c) {
OATPP_ASSERT(pair.first == "key" + oatpp::utils::conversion::int32ToStr(i))
OATPP_ASSERT(pair.second == "pair_item" + oatpp::utils::conversion::int32ToStr(i))
i++;
}
}
{
auto c = obj1->field_unordered_fields;
OATPP_ASSERT(c["key1"] == "map_item1")
OATPP_ASSERT(c["key2"] == "map_item2")
OATPP_ASSERT(c["key3"] == "map_item3")
}
result = mapper->writeToString(obj1);
OATPP_LOGV(TAG, "json='%s'", result->c_str())
}
{
auto c = obj1->field_fields;
v_int32 i = 0;
for(auto& pair : *c) {
OATPP_ASSERT(pair.first == "key" + oatpp::utils::conversion::int32ToStr(i))
OATPP_ASSERT(pair.second == "pair_item" + oatpp::utils::conversion::int32ToStr(i))
i++;
auto test2 = Test2::createShared();
oatpp::String result;
try {
result = mapper->writeToString(test2);
} catch(std::runtime_error& e) {
OATPP_LOGV(TAG, "Test2::field_string is required!")
}
OATPP_ASSERT(result == nullptr)
}
{
auto test3 = Test3::createShared();
try {
auto result = mapper->writeToString(test3);
} catch(std::runtime_error& e) {
OATPP_ASSERT(false)
}
}
{
auto c = obj1->field_unordered_fields;
OATPP_ASSERT(c["key1"] == "map_item1")
OATPP_ASSERT(c["key2"] == "map_item2")
OATPP_ASSERT(c["key3"] == "map_item3")
auto test4 = Test4::createShared();
test4->field_string = "string value";
test4->child = TestChild1::createShared();
oatpp::String result;
try {
result = mapper->writeToString(test4);
} catch(std::runtime_error& e) {
OATPP_LOGV(TAG, "TestChild1::name is required!")
}
OATPP_ASSERT(result == nullptr)
}
{
auto test5 = Test5::createShared();
test5->field_string = "string value";
test5->child = TestChild2::createShared();
try {
auto result = mapper->writeToString(test5);
} catch(std::runtime_error& e) {
OATPP_ASSERT(false)
}
}
result = mapper->writeToString(obj1);
OATPP_LOGV(TAG, "json='%s'", result->c_str())
{

View File

@ -79,6 +79,58 @@ class Test4 : public oatpp::DTO {
};
class Test5 : public oatpp::DTO {
DTO_INIT(Test5, DTO)
DTO_FIELD_INFO(strF) {
info->required = true;
}
DTO_FIELD(String, strF);
};
class Test6 : public oatpp::DTO {
DTO_INIT(Test6, DTO)
DTO_FIELD(String, strF);
};
class TestChild1 : public oatpp::DTO {
DTO_INIT(TestChild1, DTO)
DTO_FIELD_INFO(name) {
info->required = true;
}
DTO_FIELD(String, name);
};
class Test7 : public oatpp::DTO {
DTO_INIT(Test7, DTO)
DTO_FIELD(String, strF);
DTO_FIELD(Object<TestChild1>, child);
};
class TestChild2 : public oatpp::DTO {
DTO_INIT(TestChild2, DTO)
DTO_FIELD(String, name);
};
class Test8 : public oatpp::DTO {
DTO_INIT(Test8, DTO)
DTO_FIELD(String, strF);
DTO_FIELD(Object<TestChild2>, child);
};
class AnyDto : public oatpp::DTO {
DTO_INIT(AnyDto, DTO)
@ -100,7 +152,7 @@ void DeserializerTest::onRun(){
OATPP_ASSERT(obj1)
OATPP_ASSERT(!obj1->strF)
obj1 = mapper->readFromString<oatpp::Object<Test1>>("{\"strF\":\"value1\"}");
obj1 = mapper->readFromString<oatpp::Object<Test1>>(R"({"strF":"value1"})");
OATPP_ASSERT(obj1)
OATPP_ASSERT(obj1->strF)
@ -185,6 +237,34 @@ void DeserializerTest::onRun(){
OATPP_ASSERT(obj4->list->size() == 0)
OATPP_ASSERT(obj4->map->size() == 0)
data::mapping::type::DTOWrapper<Test5> obj5;
try {
obj5 = mapper->readFromString<oatpp::Object<Test5>>(R"({"strF":null})");
} catch (std::runtime_error& e) {
OATPP_LOGD(TAG, "Test5::strF is required!")
}
OATPP_ASSERT(obj5 == nullptr)
try {
auto obj6 = mapper->readFromString<oatpp::Object<Test6>>(R"({"strF":null})");
} catch (std::runtime_error& e) {
OATPP_ASSERT(false)
}
data::mapping::type::DTOWrapper<Test7> obj7;
try {
obj7 = mapper->readFromString<oatpp::Object<Test7>>(R"({"strF":"value1", "child":{"name":null}})");
} catch (std::runtime_error& e) {
OATPP_LOGD(TAG, "TestChild1::name is required!")
}
OATPP_ASSERT(obj7 == nullptr)
try {
auto obj8 = mapper->readFromString<oatpp::Object<Test8>>(R"({"strF":"value1", "child":{"name":null}})");
} catch (std::runtime_error& e) {
OATPP_ASSERT(false)
}
OATPP_LOGD(TAG, "Any: String")
{
auto dto = mapper->readFromString<oatpp::Object<AnyDto>>(R"({"any":"my_string"})");