8.0 KiB
Oat++ 1.1.0
Oat++ 1.1.0
is introducing breaking changes.
Please read carefully to prepare for migration.
Feel free to ask questions - Chat on Gitter
Contents:
- No More ObjectWrapper
- Object-Mapping Simplified Primitives
- Object-Mapping std Collections
- Type oatpp::Any
- Type oatpp::Enum
- DTO - Hashcode & Equals
- DTO - Fields Annotation
No More ObjectWrapper
ObjectWrapper
is a base-class for oatpp core types (oatpp/core/Types.hpp
).
The explicit ::ObjectWrapper
qualifier is removed.
DTO:
class MyDto : oatpp::DTO { // <--- Notice the 'oatpp::DTO' now. NOT the 'oatpp::Object'
DTO_INIT(MyDto, DTO)
DTO_FIELD(Object<MyDto>, nested); // <--- Notice the oatpp::Object<T> for Objects, instead of T::ObjectWrapper
DTO_FIELD(List<Any>, listOfAny); // <--- No '::ObjectWrapper' for collections.
DTO_FIELD(Fields<List<String>>, mapOfLists);
}
ApiController:
ENDPOINT("POST", "body-dto", postWithBody,
BODY_DTO(Object<MyDto>, body)) {
...
}
ApiClient:
API_CALL("POST", "body-dto", postWithBody, BODY_DTO(Object<MyDto>, body))
In Other Code-Blocks
oatpp::Object<MyDto> myDto;
oatpp::List<oatpp::Object<MyDto>> listOfDtos;
...
auto myDto = objectMapper->readFromString<Object<MyDto>>(json);
auto listOfDtos = objectMapper->readFromString<List<Object<MyDto>>>(json);
Object-Mapping Simplified Primitives
No more <primitive>->getValue()
.
oatpp::Int32 objV = 32;
v_int32 v = objV; // You may check for nullptr before doing this
bool equals = v == objV; // <--- NO NEED to check for nullptr here
bool isNull = objV == nullptr;
bool isNull = !objV;
Object-Mapping std Collections
Now oatpp::<mapping-enabled-collections>
are based on std::<collections>
Example:
oatpp::Vector<oatpp::String> vector({});
oatpp::List<oatpp::String> list({});
oatpp::UnorderedSet<oatpp::String> set({});
oatpp::Fields<oatpp::String> pairList({});
oatpp::UnorderedFields<oatpp::String> hashMap({});
oatpp::Vector<oatpp::String> vector = {"a", "b", "c"};
oatpp::List<oatpp::String> list = {"a", "b", "c"};
oatpp::UnorderedSet<oatpp::String> set = {"a", "b", "c"};
oatpp::Fields<oatpp::String> pairList = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}};
oatpp::UnorderedFields<oatpp::String> hashMap = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}};
vector[0] = "z"; // <--- Complexity = O(1);
vector->push_back("www");
list[0] = "z"; // <--- Complexity = O(n);
list->push_back("www");
bool contains = set["b"]; // <--- Complexity = O(1);
set->insert("z")
auto value = pairList.getValueByKey("k1"); // <--- Complexity = O(n); // note '.' here, not '->' !!!
pairList->push_back({"key_z", "z"});
hashMap["k1"] = "z" // <--- Complexity = O(1);
hashMap->insert({"key_z", "z"});
for(auto& item : *vector) {
...
}
for(auto& item : *list) {
...
}
for(auto& item : *set) {
...
}
for(auto& pair : *pairList) {
...
}
for(auto& pair : *hashMap) {
...
}
Type oatpp::Any
The new Type Introduced - oatpp::Any
.
Now it's possible to do like this:
class MyDto : public oatpp::DTO {
DTO_INIT(MyDto, DTO)
DTO_FIELD(Any, any); // Put any oatpp::<Type> here
DTO_FIELD(List<Any>, listOfAny)
DTO_FIELD(Fields<Any>, mapOfAny);
};
JSON Serializer
Will serialize Any depending on the value type it stores.
JSON Deserializer
Will try to guess the type of the Any
.
Currently, Any
is deserialized as follows:
- JSON objects are deserialized to
Any
holdingoatpp::Fields<Any>
. - JSON lists are deserialized to
Any
holdingoatpp::List<Any>
. - JSON
null
is deserialized toAny
holdingnullptr
. - JSON
true
/false
is deserialized toAny
holdingoatpp::Boolean
. - JSON
number
is deserialized toAny
holdingoatpp::Float64
.
Example
oatpp::Fields<oatpp::Any> map = {
{"title", oatpp::String("Hello Any!")},
{"listOfAny",
oatpp::List<oatpp::Any>({
oatpp::Int32(32),
oatpp::Float32(0.32),
oatpp::Boolean(true)
})
}
};
auto json = mapper->writeToString(map);
Output:
{
"title": "Hello Any!",
"listOfAny": [
32,
0.3199999928474426,
true
]
}
Object-Mapping Enum
Enum is added to DTO codegen.
Declaration
#include OATPP_CODEGEN_BEGIN(DTO)
ENUM(Enum1, v_int32, // <-- type is mandatory
VALUE(V_1, 1), // <-- integral value is mandatory
VALUE(V_2, 2, "name-2"),
VALUE(V_3, 3, "name-3", "description_3")
)
#include OATPP_CODEGEN_END(DTO)
This will generate:
enum class Enum1 : v_int32 {
V_1 = 1,
V_2 = 2,
V_3 = 3
}
... // PLUS Meta here
Current limitation - ENUM
can not be declared nested in the class :(.
This may be fixed later.
Usage
DTO
ENUM(MyEnum, v_int32,
VALUE(V1, 10, "enum1-v1"),
VALUE(V2, 20, "enum1-v2"),
VALUE(V3, 30, "enum1-v3")
);
class MyDto : public oatpp::DTO {
DTO_INIT(MyDto, DTO)
DTO_FIELD(Enum<MyEnum>, enum1); // Default interpretation - AsString
DTO_FIELD(Enum<MyEnum>::NotNull, enum2); // NOT_NULL constraint for Ser/De
DTO_FIELD(Enum<MyEnum>::AsString, enum3); // Ser/De as string name
DTO_FIELD(Enum<MyEnum>::AsNumber, enum4); // Ser/De as a corresponding integral value
DTO_FIELD(Enum<MyEnum>::AsString::NotNull, enum5);
DTO_FIELD(Enum<MyEnum>::AsNumber::NotNull, enum6);
};
ApiController
ENDPOINT("GET", "enum/as-string", testEnumString,
HEADER(Enum<AllowedHeaderValues>::AsString, enumValue, "X-MyEnum"))
{
return createResponse(Status::CODE_200, "OK");
}
ENDPOINT("GET", "enum/as-number", testEnumNumber,
HEADER(Enum<AllowedHeaderValues>::AsNumber, enumValue, "X-MyEnum"))
{
return createResponse(Status::CODE_200, "OK");
}
ApiClient
API_CALL("GET", "enum/as-string", getWithEnumHeaderAsString, HEADER(Enum<AllowedHeaderValues>::AsString, enumValue, "X-MyEnum"))
API_CALL("GET", "enum/as-number", getWithEnumHeaderAsNumber, HEADER(Enum<AllowedHeaderValues>::AsNumber, enumValue, "X-MyEnum"))
Meta functions
{
auto entry = oatpp::Enum<MyEnum>::getEntryByName("<name>");
auto entry = oatpp::Enum<MyEnum>::getEntryByValue(MyEnum::VALUE);
auto entry = oatpp::Enum<MyEnum>::getEntryByUnderlyingValue(123);
auto entry = oatpp::Enum<MyEnum>::getEntryByIndex(0);
...
OATPP_LOGD("Entry", "%d, %s, %d, %d, %s", entry.index, entry.name, entry.value, entry.description);
}
{
const auto& entries = oatpp::Enum<MyEnum>::getEntries();
for(const auto& e : entries) {
...
}
}
DTO Hashcode and Equals
Now DTOs can be used as a Key in unordered_map
and unordered_set
.
The convenience DTO_HC_EQ
(DTO_HASHCODE_AND_EQUALS) macro has been added.
class User : public oatpp::DTO {
DTO_INIT(User, DTO)
DTO_FIELD(String, firstName);
DTO_FIELD(String, lastName);
DTO_HC_EQ(firstName, lastName) // List key fields that count in std::hash and "==","!=" operators.
};
The DTO_HC_EQ
macro works taking into account the DTO_HC_EQ
declared in the parent DTO class.
If no DTO_HC_EQ
is declared in none of the DTO's parent classes the default behavior is:
std::hash
- isv_uint64
representation of object address.- operators
==
and!=
- is comparison of object addresses.
DTO Fields Annotation
Now it's possible to add a description for DTO fields, which will be automatically displayed in swagger-UI.
class MyDto : public oatpp::DTO {
DTO_INIT(MyDto, DTO)
DTO_FIELD_INFO(id) {
info->description = "identifier";
}
DTO_FIELD(String, id);
};
Note: The description
is currently the only info you can add to the DTO field
(This may be extended later). In order to provide the list of possible values - use the
new Enum feature - Type oatpp::Enum.