# 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](https://gitter.im/oatpp-framework/Lobby) Contents: - [No More ObjectWrapper](#no-more-objectwrapper) - [Object-Mapping Simplified Primitives](#object-mapping-simplified-primitives) - [Object-Mapping std Collections](#object-mapping-std-collections) - [Type oatpp::Any](#type-oatppany) - [Type oatpp::Enum](#object-mapping-enum) - [DTO - Hashcode & Equals](#dto-hashcode-and-equals) - [DTO - Fields Annotation](#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: ```cpp class MyDto : oatpp::DTO { // <--- Notice the 'oatpp::DTO' now. NOT the 'oatpp::Object' DTO_INIT(MyDto, DTO) DTO_FIELD(Object, nested); // <--- Notice the oatpp::Object for Objects, instead of T::ObjectWrapper DTO_FIELD(List, listOfAny); // <--- No '::ObjectWrapper' for collections. DTO_FIELD(Fields>, mapOfLists); } ``` ### ApiController: ```cpp ENDPOINT("POST", "body-dto", postWithBody, BODY_DTO(Object, body)) { ... } ``` ### ApiClient: ```cpp API_CALL("POST", "body-dto", postWithBody, BODY_DTO(Object, body)) ``` ### In Other Code-Blocks ```cpp oatpp::Object myDto; oatpp::List> listOfDtos; ... auto myDto = objectMapper->readFromString>(json); auto listOfDtos = objectMapper->readFromString>>(json); ``` ## Object-Mapping Simplified Primitives No more `->getValue()`. ```cpp 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::` are based on `std::` Example: ```cpp oatpp::Vector vector({}); oatpp::List list({}); oatpp::UnorderedSet set({}); oatpp::Fields pairList({}); oatpp::UnorderedFields hashMap({}); oatpp::Vector vector = {"a", "b", "c"}; oatpp::List list = {"a", "b", "c"}; oatpp::UnorderedSet set = {"a", "b", "c"}; oatpp::Fields pairList = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}; oatpp::UnorderedFields 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: ```cpp class MyDto : public oatpp::DTO { DTO_INIT(MyDto, DTO) DTO_FIELD(Any, any); // Put any oatpp:: here DTO_FIELD(List, listOfAny) DTO_FIELD(Fields, 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` holding `oatpp::Fields`. - JSON lists are deserialized to `Any` holding `oatpp::List`. - JSON `null` is deserialized to `Any` holding `nullptr`. - JSON `true`/`false` is deserialized to `Any` holding `oatpp::Boolean`. - JSON `number` is deserialized to `Any` holding `oatpp::Float64`. ### Example ```cpp oatpp::Fields map = { {"title", oatpp::String("Hello Any!")}, {"listOfAny", oatpp::List({ oatpp::Int32(32), oatpp::Float32(0.32), oatpp::Boolean(true) }) } }; auto json = mapper->writeToString(map); ``` **Output:** ```json { "title": "Hello Any!", "listOfAny": [ 32, 0.3199999928474426, true ] } ``` ## Object-Mapping Enum Enum is added to DTO codegen. ### Declaration ```cpp #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: ```cpp 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 ```cpp 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, enum1); // Default interpretation - AsString DTO_FIELD(Enum::NotNull, enum2); // NOT_NULL constraint for Ser/De DTO_FIELD(Enum::AsString, enum3); // Ser/De as string name DTO_FIELD(Enum::AsNumber, enum4); // Ser/De as a corresponding integral value DTO_FIELD(Enum::AsString::NotNull, enum5); DTO_FIELD(Enum::AsNumber::NotNull, enum6); }; ``` #### ApiController ```cpp ENDPOINT("GET", "enum/as-string", testEnumString, HEADER(Enum::AsString, enumValue, "X-MyEnum")) { return createResponse(Status::CODE_200, "OK"); } ENDPOINT("GET", "enum/as-number", testEnumNumber, HEADER(Enum::AsNumber, enumValue, "X-MyEnum")) { return createResponse(Status::CODE_200, "OK"); } ``` #### ApiClient ```cpp API_CALL("GET", "enum/as-string", getWithEnumHeaderAsString, HEADER(Enum::AsString, enumValue, "X-MyEnum")) API_CALL("GET", "enum/as-number", getWithEnumHeaderAsNumber, HEADER(Enum::AsNumber, enumValue, "X-MyEnum")) ``` ### Meta functions ```cpp { auto entry = oatpp::Enum::getEntryByName(""); auto entry = oatpp::Enum::getEntryByValue(MyEnum::VALUE); auto entry = oatpp::Enum::getEntryByUnderlyingValue(123); auto entry = oatpp::Enum::getEntryByIndex(0); ... OATPP_LOGD("Entry", "%d, %s, %d, %d, %s", entry.index, entry.name, entry.value, entry.description) } { const auto& entries = oatpp::Enum::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. ```cpp 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` - is `v_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. ```cpp 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](#object-mapping-enum).*