diff --git a/docs/mkdocs/docs/tutorials/index.md b/docs/mkdocs/docs/tutorials/index.md index a8fd1746..99153d80 100644 --- a/docs/mkdocs/docs/tutorials/index.md +++ b/docs/mkdocs/docs/tutorials/index.md @@ -138,7 +138,7 @@ novels: year: 1678 - title: Frankenstein - author: Jane Austen + author: Mary Shelly year: 1818 - title: Moby-Dick @@ -245,7 +245,7 @@ recommends: author: Daniel Defoe - title: Frankenstein - author: Jane Austen + author: Mary Shelly - title: Moby-Dick author: Herman Melville @@ -259,12 +259,12 @@ recommends: ### :pill: Integrate with user-defined types -As described in the API Reference pages for [`from_node()`](../api/node_value_converter/from_node.md) and [`to_node()`](../api/node_value_converter/to_node.md) functions, you can specialize deserialization for user-defined types. +As described in the API Reference pages for [`from_node()`](../api/node_value_converter/from_node.md) and [`to_node()`](../api/node_value_converter/to_node.md) functions, you can specialize serialization and deserialization for user-defined types. Note that you don't need to implement specializations for STL types (such as std::vector or std::string) because the fkYAML library has already implemented them. The updated code snippet down below shows how the specializations for user-defined types can reduce boilerplate code. -```cpp title="tutorial.cpp" hl_lines="6-38 55-60" +```cpp title="tutorial.cpp" hl_lines="6-39 53-54 56-57 59-61" #include #include #include @@ -292,6 +292,7 @@ void from_node(const fkyaml::node& node, novel& novel) { novel.title = node["title"].get_value_ref(); novel.author = node["author"].get_value_ref(); + novel.year = node["year"].get_value(); } void to_node(fkyaml::node& node, const recommend& recommend) @@ -316,12 +317,12 @@ int main() fkyaml::node response = { "recommends", fkyaml::node::sequence() }; auto& recommends = response["recommends"].get_value_ref(); + // get novels directly from the node. + auto novels = root["novels"].get_value>(); + // generate recommendations by extracting "title" & "author" values. - for (auto& novel_node : root["novels"]) + for (auto& novel : novels) { - // get novel directly from the node. - ns::novel novel = novel_node.get_value(); - // create a recommendation node directly with a recommend object. ns::recommend recommend = { std::move(novel.title), std::move(novel.author) }; recommends.emplace_back(recommend); diff --git a/include/fkYAML/detail/conversions/from_node.hpp b/include/fkYAML/detail/conversions/from_node.hpp index 14089239..9840fc36 100644 --- a/include/fkYAML/detail/conversions/from_node.hpp +++ b/include/fkYAML/detail/conversions/from_node.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -58,6 +59,37 @@ inline void from_node(const BasicNodeType& n, typename BasicNodeType::sequence_t s = n.template get_value_ref(); } +/** + * @brief from_node function for objects of the std::vector of compatible types. + * + * @tparam BasicNodeType A basic_node template instance type. + * @tparam CompatibleValueType A compatible type for BasicNodeType. + * @param n A basic_node object. + * @param s A vector of compatible type objects. + */ +template < + typename BasicNodeType, typename CompatibleValueType, + enable_if_t< + conjunction< + is_basic_node, negation>, + has_from_node, + negation, typename BasicNodeType::sequence_type>>>::value, + int> = 0> +inline void from_node(const BasicNodeType& n, std::vector& s) +{ + if (!n.is_sequence()) + { + throw exception("The target node value is not sequence type."); + } + + s.reserve(n.size()); + + for (const auto& elem : n) + { + s.emplace_back(elem.template get_value()); + } +} + /** * @brief from_node function for BasicNodeType::mapping_type objects. * diff --git a/test/unit_test/CMakeLists.txt b/test/unit_test/CMakeLists.txt index 6f1c2180..7128d7df 100644 --- a/test/unit_test/CMakeLists.txt +++ b/test/unit_test/CMakeLists.txt @@ -150,6 +150,7 @@ set(TEST_TARGET "fkYAMLUnitTest") add_executable( ${TEST_TARGET} + test_custom_from_node.cpp test_deserializer_class.cpp test_exception_class.cpp test_from_string.cpp diff --git a/test/unit_test/test_custom_from_node.cpp b/test/unit_test/test_custom_from_node.cpp new file mode 100644 index 00000000..cf4446a1 --- /dev/null +++ b/test/unit_test/test_custom_from_node.cpp @@ -0,0 +1,79 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.2.0 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include + +#include + +#include + +namespace test +{ + +struct novel +{ + std::string title; + std::string author; + int year; +}; + +void from_node(const fkyaml::node& node, novel& novel) +{ + novel.title = node["title"].get_value_ref(); + novel.author = node["author"].get_value_ref(); + novel.year = node["year"].get_value(); +} + +} // namespace test + +TEST_CASE("FromNodeTest_UserDefinedTypeTest", "[FromNodeTest]") +{ + std::string input = "title: Robinson Crusoe\n" + "author: Daniel Defoe\n" + "year: 1678"; + fkyaml::node node = fkyaml::node::deserialize(input); + + auto novel = node.get_value(); + REQUIRE(novel.title == "Robinson Crusoe"); + REQUIRE(novel.author == "Daniel Defoe"); + REQUIRE(novel.year == 1678); +} + +TEST_CASE("FromNodeTest_UserDefinedTypeVectorTest", "[FromNodeTest]") +{ + std::string input = "novels:\n" + " - title: Robinson Crusoe\n" + " author: Daniel Defoe\n" + " year: 1678\n" + " - title: Frankenstein\n" + " author: Mary Shelly\n" + " year: 1818\n"; + fkyaml::node node = fkyaml::node::deserialize(input); + + auto novels = node["novels"].get_value>(); + REQUIRE(novels[0].title == "Robinson Crusoe"); + REQUIRE(novels[0].author == "Daniel Defoe"); + REQUIRE(novels[0].year == 1678); + REQUIRE(novels[1].title == "Frankenstein"); + REQUIRE(novels[1].author == "Mary Shelly"); + REQUIRE(novels[1].year == 1818); +} + +TEST_CASE("FromNodeTest_UserDefinedTypeVectorErrorTest", "[FromNodeTest]") +{ + std::string input = "novels:\n" + " - title: Robinson Crusoe\n" + " author: Daniel Defoe\n" + " year: 1678\n" + " - title: Frankenstein\n" + " author: Mary Shelly\n" + " year: 1818\n"; + fkyaml::node node = fkyaml::node::deserialize(input); + REQUIRE_THROWS_AS(node.get_value>(), fkyaml::exception); +} \ No newline at end of file