Add serialization and deserialization for vector

This commit is contained in:
sha512sum 2024-11-10 14:56:41 +00:00 committed by Anton
parent efcbc8e86a
commit eafa3aff85
3 changed files with 88 additions and 2 deletions

View file

@ -4,6 +4,7 @@
#include <larra/serialization/error.hpp>
#include <nameof.hpp>
#include <ranges>
#include <string>
#include <utempl/utils.hpp>
@ -169,8 +170,8 @@ struct Serialization<std::variant<Ts...>> : SerializationBase<> {
}
static constexpr auto Serialize(xmlpp::Element* element, const std::variant<Ts...>& object) -> void {
std::visit(
[&](const auto& object) {
element << object;
[&]<typename T>(const T& object) {
Serialization<T>::Serialize(element, object);
},
object);
}
@ -191,4 +192,42 @@ struct Serialization<std::monostate> : SerializationBase<> {
}
};
template <typename T>
struct Serialization<std::vector<T>> : SerializationBase<> {
static constexpr auto Parse(xmlpp::Element* element) -> std::vector<T> {
return element->get_children(Serialization<T>::kDefaultName) | std::views::transform([](xmlpp::Node* node) {
auto itemElement = dynamic_cast<xmlpp::Element*>(node);
if(!itemElement) {
throw serialization::ParsingError{"Can't convert xmlpp::Node to xmlpp::Element ]"};
}
return Serialization<T>::Parse(itemElement);
}) |
std::ranges::to<std::vector<T>>();
}
static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<std::vector<T>> {
auto chd = element->get_children(Serialization<T>::kDefaultName);
auto range = chd | std::views::transform([](xmlpp::Node* node) -> std::optional<T> {
auto itemElement = dynamic_cast<xmlpp::Element*>(node);
if(!itemElement) {
return std::nullopt;
}
return Serialization<T>::TryParse(itemElement);
});
std::vector<T> response;
response.reserve(chd.size());
for(auto& value : range) {
if(!value) {
return std::nullopt;
}
response.push_back(std::move(*value));
}
return response;
}
static constexpr auto Serialize(xmlpp::Element* node, const std::vector<T>& value) -> void {
for(const T& element : value) {
Serialization<T>::Serialize(node->add_child_element(Serialization<T>::kDefaultName), element);
}
}
};
} // namespace larra::xmpp

View file

@ -4,6 +4,7 @@
#include <boost/pfr.hpp>
#include <larra/serialization.hpp>
#include <larra/serialization/error.hpp>
#include <ranges>
#include <utempl/constexpr_string.hpp>
#include <utempl/tuple.hpp>
#include <utempl/utils.hpp>
@ -154,6 +155,25 @@ struct ElementSerializer {
}
};
template <typename T, auto& Config, typename Info>
struct ElementSerializer<std::vector<T>, Config, Info> {
static constexpr auto Parse(xmlpp::Element* element) {
try {
return Serialization<std::vector<T>>::Parse(element);
} catch(const std::exception& error) {
throw ElementParsingError(
std::format("[{}: {}] parsing error: [ {} ]", Info::kName, nameof::nameof_full_type<std::vector<T>>(), error.what()));
}
}
static constexpr auto Serialize(xmlpp::Element* node, const std::vector<T>& element) {
try {
return Serialization<std::vector<T>>::Serialize(node, element);
} catch(const std::exception& error) {
throw ElementSerializaionError(
std::format("[{}: {}] serialization error: [ {} ]", Info::kName, nameof::nameof_full_type<std::vector<T>>(), error.what()));
}
}
};
namespace impl {
template <typename T>

View file

@ -37,6 +37,7 @@ struct SomeStruct {
std::string value;
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> SomeStruct;
friend auto operator<<(xmlpp::Element* element, const SomeStruct& self);
constexpr auto operator==(const SomeStruct&) const -> bool = default;
};
struct SomeStruct2 {
@ -65,6 +66,12 @@ struct SomeStruct5 {
friend auto operator<<(xmlpp::Element* element, const SomeStruct5& self);
};
struct SomeStruct6 {
static constexpr auto kDefaultName = "some6";
std::vector<SomeStruct> some;
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> SomeStruct6;
};
} // namespace tests::serialization
namespace serialization {
@ -81,6 +88,9 @@ constexpr auto kSerializationConfig<tests::serialization::SomeStruct4> = Seriali
template <>
constexpr auto kSerializationConfig<tests::serialization::SomeStruct5> = SerializationConfig<tests::serialization::SomeStruct5>{};
template <>
constexpr auto kSerializationConfig<tests::serialization::SomeStruct6> =
SerializationConfig<tests::serialization::SomeStruct6>{}.With<"some">({Config<std::vector<tests::serialization::SomeStruct>>{}});
} // namespace serialization
namespace tests::serialization {
@ -105,6 +115,10 @@ auto SomeStruct5::Parse(xmlpp::Element* element) -> SomeStruct5 {
return ::larra::xmpp::serialization::Parse<tests::serialization::SomeStruct5>(element);
}
auto SomeStruct6::Parse(xmlpp::Element* element) -> SomeStruct6 {
return ::larra::xmpp::serialization::Parse<tests::serialization::SomeStruct6>(element);
}
auto operator<<(xmlpp::Element* element, const SomeStruct& self) {
::larra::xmpp::serialization::Serialize(element, self);
}
@ -144,6 +158,19 @@ TEST(AutoParse, Attribute) {
EXPECT_EQ(a.value.username, "user"sv);
}
TEST(AutoParse, Vector) {
xmlpp::Document doc;
auto node = doc.create_root_node("some6");
for(auto i : std::views::iota(0, 10)) {
auto child = node->add_child_element("some");
child->set_attribute("value", std::format("Hello {}", i));
}
auto value = Serialization<tests::serialization::SomeStruct6>::Parse(node);
EXPECT_EQ(value.some, std::views::iota(0, 10) | std::views::transform([](auto i) -> tests::serialization::SomeStruct {
return {.value = std::format("Hello {}", i)};
}) | std::ranges::to<std::vector<tests::serialization::SomeStruct>>());
}
TEST(AutoSerialize, Basic) {
xmlpp::Document doc;
auto node = doc.create_root_node("some2");