Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

#190 Support specialization of deserialization for a vector of user-defined type objects #203

Merged
merged 3 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions docs/mkdocs/docs/tutorials/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ novels:
year: 1678
-
title: Frankenstein
author: Jane Austen
author: Mary Shelly
year: 1818
-
title: Moby-Dick
Expand Down Expand Up @@ -245,7 +245,7 @@ recommends:
author: Daniel Defoe
-
title: Frankenstein
author: Jane Austen
author: Mary Shelly
-
title: Moby-Dick
author: Herman Melville
Expand All @@ -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 <fstream>
#include <iostream>
#include <utility>
Expand Down Expand Up @@ -292,6 +292,7 @@ void from_node(const fkyaml::node& node, novel& novel)
{
novel.title = node["title"].get_value_ref<const std::string&>();
novel.author = node["author"].get_value_ref<const std::string&>();
novel.year = node["year"].get_value<int>();
}

void to_node(fkyaml::node& node, const recommend& recommend)
Expand All @@ -316,12 +317,12 @@ int main()
fkyaml::node response = { "recommends", fkyaml::node::sequence() };
auto& recommends = response["recommends"].get_value_ref<fkyaml::node::sequence_type&>();

// get novels directly from the node.
auto novels = root["novels"].get_value<std::vector<ns::novel>>();

// 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<ns::novel>();

// create a recommendation node directly with a recommend object.
ns::recommend recommend = { std::move(novel.title), std::move(novel.author) };
recommends.emplace_back(recommend);
Expand Down
32 changes: 32 additions & 0 deletions include/fkYAML/detail/conversions/from_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <limits>
#include <utility>
#include <vector>

#include <fkYAML/detail/macros/version_macros.hpp>
#include <fkYAML/detail/meta/node_traits.hpp>
Expand Down Expand Up @@ -58,6 +59,37 @@ inline void from_node(const BasicNodeType& n, typename BasicNodeType::sequence_t
s = n.template get_value_ref<const typename BasicNodeType::sequence_type&>();
}

/**
* @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<BasicNodeType>, negation<is_basic_node<CompatibleValueType>>,
has_from_node<BasicNodeType, CompatibleValueType>,
negation<std::is_same<std::vector<CompatibleValueType>, typename BasicNodeType::sequence_type>>>::value,
int> = 0>
inline void from_node(const BasicNodeType& n, std::vector<CompatibleValueType>& 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<CompatibleValueType>());
}
}

/**
* @brief from_node function for BasicNodeType::mapping_type objects.
*
Expand Down
1 change: 1 addition & 0 deletions test/unit_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
79 changes: 79 additions & 0 deletions test/unit_test/test_custom_from_node.cpp
Original file line number Diff line number Diff line change
@@ -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 <fktn.dev@gmail.com>
// SPDX-License-Identifier: MIT

#include <string>
#include <vector>

#include <catch2/catch.hpp>

#include <fkYAML/node.hpp>

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<const std::string&>();
novel.author = node["author"].get_value_ref<const std::string&>();
novel.year = node["year"].get_value<int>();
}

} // 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<test::novel>();
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<std::vector<test::novel>>();
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<std::vector<test::novel>>(), fkyaml::exception);
}