
.. _program_listing_file_zeem_serialize.hpp:

Program Listing for File serialize.hpp
======================================

|exhale_lsh| :ref:`Return to documentation for file <file_zeem_serialize.hpp>` (``zeem/serialize.hpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   /*-
    * SPDX-License-Identifier: BSD-2-Clause
    *
    * Copyright (c) 2024 Maarten L. Hekkelman
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions are met:
    *
    * 1. Redistributions of source code must retain the above copyright notice, this
    *    list of conditions and the following disclaimer
    * 2. Redistributions in binary form must reproduce the above copyright notice,
    *    this list of conditions and the following disclaimer in the documentation
    *    and/or other materials provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    */
   
   #pragma once
   
   
   #include "zeem/config.hpp"
   #include "zeem/detail/charconv.hpp"
   #include "zeem/node.hpp"
   
   #if ZEEM_USE_DATE_H
   # include <date/date.h>
   # include <date/tz.h>
   #endif
   
   #include <algorithm>
   #include <charconv>
   #include <chrono>
   #include <map>
   #include <optional>
   #include <regex>
   #include <source_location>
   #include <string>
   #include <system_error>
   
   namespace zeem
   {
   
   // --------------------------------------------------------------------
   
   template <typename T>
   struct value_serializer;
   
   template <>
   struct value_serializer<bool>
   {
       static constexpr std::string type_name() { return "xsd:boolean"; }
       static constexpr std::string to_string(bool value) { return value ? "true" : "false"; }
       static constexpr bool from_string(std::string_view value) { return value == "true" or value == "1" or value == "yes"; }
   };
   
   template <>
   struct value_serializer<std::string>
   {
       static constexpr std::string type_name() { return "xsd:string"; }
       static constexpr std::string to_string(std::string value) { return value; }
       static constexpr std::string from_string(std::string_view value) { return std::string{ value }; }
   };
   
   template <typename T>
   struct char_conv_serializer
   {
       using value_type = T;
   
       static constexpr std::string derived_type_name()
       {
           using value_serializer_type = value_serializer<value_type>;
           return value_serializer_type::type_name();
       }
   
       static std::string to_string(value_type value)
       {
           char b[32];
           if (auto r = std::to_chars(b, b + sizeof(b), value); r.ec == std::errc{})
               return { b, r.ptr };
           else
               throw std::system_error(std::make_error_code(r.ec), "Error converting value to string for type " + derived_type_name());
       }
   
       static value_type from_string(std::string_view value)
       {
           value_type result{};
   
           auto r = from_chars(value.data(), value.data() + value.length(), result);
           if (r.ec != std::errc{} or r.ptr != value.data() + value.length())
               throw std::system_error(std::make_error_code(r.ec), "Error converting value '" + std::string{ value } + "' to type " + derived_type_name());
   
           return result;
       }
   };
   
   template <>
   struct value_serializer<int8_t> : char_conv_serializer<int8_t>
   {
       static std::string type_name() { return "xsd:byte"; }
   };
   
   template <>
   struct value_serializer<uint8_t> : char_conv_serializer<uint8_t>
   {
       static std::string type_name() { return "xsd:unsignedByte"; }
   };
   
   template <>
   struct value_serializer<int16_t> : char_conv_serializer<int16_t>
   {
       static std::string type_name() { return "xsd:short"; }
   };
   
   template <>
   struct value_serializer<uint16_t> : char_conv_serializer<uint16_t>
   {
       static std::string type_name() { return "xsd:unsignedShort"; }
   };
   
   template <>
   struct value_serializer<int32_t> : char_conv_serializer<int32_t>
   {
       static std::string type_name() { return "xsd:int"; }
   };
   
   template <>
   struct value_serializer<uint32_t> : char_conv_serializer<uint32_t>
   {
       static std::string type_name() { return "xsd:unsignedInt"; }
   };
   
   template <>
   struct value_serializer<int64_t> : char_conv_serializer<int64_t>
   {
       static std::string type_name() { return "xsd:long"; }
   };
   
   template <>
   struct value_serializer<uint64_t> : char_conv_serializer<uint64_t>
   {
       static std::string type_name() { return "xsd:unsignedLong"; }
   };
   
   template <>
   struct value_serializer<float> : char_conv_serializer<float>
   {
       static std::string type_name() { return "xsd:float"; }
   };
   
   template <>
   struct value_serializer<double> : char_conv_serializer<double>
   {
       static std::string type_name() { return "xsd:double"; }
   };
   
   
   template <typename T>
       requires std::is_enum_v<T>
   struct value_serializer<T>
   {
       using value_map_type = std::map<T, std::string>;
       using value_map_value_type = typename value_map_type::value_type;
   
     private:
       std::string m_type_name;
       value_map_type m_value_map;
   
     public:
       static void init(std::string_view name, std::initializer_list<value_map_value_type> values)
       {
           instance(std::string{ name }).m_value_map = value_map_type(values);
       }
   
       static void init(std::initializer_list<value_map_value_type> values)
       {
           instance().m_value_map = value_map_type(values);
       }
   
       static value_serializer &instance(std::string name = {})
       {
           static value_serializer s_instance;
           if (not name.empty() and s_instance.m_type_name.empty())
               s_instance.m_type_name = std::move(name);
           return s_instance;
       }
   
       value_serializer &operator()(T v, std::string_view name)
       {
           m_value_map[v] = name;
           return *this;
       }
   
       value_serializer &operator()(std::string name, T v)
       {
           m_value_map[v] = std::move(name);
           return *this;
       }
   
       static std::string type_name()
       {
           return instance().m_type_name;
       }
   
       static std::string to_string(T value)
       {
           return instance().m_value_map[value];
       }
   
       static T from_string(std::string_view value)
       {
           T result = {};
           for (auto &t : instance().m_value_map)
               if (t.second == value)
               {
                   result = t.first;
                   break;
               }
           return result;
       }
   
       static bool empty()
       {
           return instance().m_value_map.empty();
       }
   };
   
   
   // --------------------------------------------------------------------
   // date/time support
   
   
   template <>
   struct value_serializer<std::chrono::system_clock::time_point>
   {
       using time_type = std::chrono::system_clock::time_point;
   
       static std::string type_name() { return "xsd:dateTime"; }
   
       static std::string to_string(const time_type &v)
       {
           return std::format("{0:%F}T{0:%T}Z", v);
       }
   
       static time_type from_string(std::string_view s)
       {
           time_type result;
   
           std::regex kRX(R"(^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)(Z|([-+]\d{2})(?::(\d{2}))?)?)");
           std::cmatch m;
   
           if (not std::regex_match(s.data(), s.data() + s.length(), m, kRX))
               throw std::runtime_error("Invalid date format");
   
           std::istringstream is{ m[1] };
   
           // Unfortunately, from_stream never seems to return the correct time zone and offset info
           // so we add it ourselves
   
   #if ZEEM_USE_DATE_H
           date::from_stream(is, "%FT%T", result);
   #else
           std::chrono::from_stream(is, "%FT%T", result);
   #endif
   
           if (m[2].matched)
           {
               if (m[3].matched)
                   result += std::chrono::hours{ stoi(m[3]) };
               if (m[4].matched)
                   result += std::chrono::minutes{ stoi(m[4]) };
           }
           else
   #if ZEEM_USE_DATE_H
               result = date::zoned_time(date::current_zone(), result).get_sys_time();
   #else
               result = std::chrono::zoned_time{ std::chrono::current_zone(), result };
   #endif
   
           if (is.bad() or is.fail())
               throw std::runtime_error("invalid formatted date");
   
           return result;
       }
   };
   
   
   template <>
   struct value_serializer<std::chrono::sys_days>
   {
       static std::string type_name() { return "xsd:date"; }
   
       static std::string to_string(const std::chrono::sys_days &v)
       {
           return std::format("{:%F}", v);
       }
   
       static std::chrono::sys_days from_string(std::string_view s)
       {
           std::chrono::sys_days result;
   
           std::stringstream is;
           is << s;
   
   #if ZEEM_USE_DATE_H
           date::from_stream(is, "%F", result);
   #else
           std::chrono::from_stream(is, "%F", result);
   #endif
   
           if (is.bad() or is.fail())
               throw std::runtime_error("invalid formatted date");
   
           return result;
       }
   };
   
   
   template <typename T>
   using serialize_value_t = decltype(std::declval<value_serializer<T> &>().from_string(std::declval<std::string_view>()));
   
   template <typename T, typename Archive>
   using serialize_function = decltype(std::declval<T &>().serialize(std::declval<Archive &>(), std::declval<uint64_t>()));
   
   template <typename T, typename Archive, typename = void>
   struct has_serialize : std::false_type
   {
   };
   
   template <typename T, typename Archive>
       requires(std::is_class_v<T>)
   struct has_serialize<T, Archive>
   {
       static constexpr bool value = detail::is_detected_v<serialize_function, T, Archive>;
   };
   
   template <typename T, typename S>
   inline constexpr bool has_serialize_v = has_serialize<T, S>::value;
   
   template <typename T, typename S, typename = void>
   struct is_serializable_array_type : std::false_type
   {
   };
   
   template <typename T>
   using value_type_t = typename T::value_type;
   
   template <typename T>
   using iterator_t = typename T::iterator;
   
   template <typename T>
   using std_string_npos_t = decltype(T::npos);
   
   template <typename T, typename S>
   struct is_serializable_type
   {
       using value_type = std::remove_cvref_t<T>;
       static constexpr bool value =
           detail::is_detected_v<serialize_value_t, value_type> or
           has_serialize_v<value_type, S>;
   };
   
   template <typename T, typename S>
   inline constexpr bool is_serializable_type_v = is_serializable_type<T, S>::value;
   
   template <typename T, typename S>
       requires(
           detail::is_detected_v<value_type_t, T> and
           detail::is_detected_v<iterator_t, T> and
           not detail::is_detected_v<std_string_npos_t, T>)
   struct is_serializable_array_type<T, S>
   {
       static constexpr bool value = is_serializable_type_v<typename T::value_type, S>;
   };
   
   template <typename T, typename S>
   inline constexpr bool is_serializable_array_type_v = is_serializable_array_type<T, S>::value;
   
   // --------------------------------------------------------------------
   
   struct serializer;
   struct deserializer;
   
   template <typename T>
   class name_value_pair
   {
     public:
       name_value_pair(std::string name, T &value)
           : m_name(std::move(name))
           , m_value(value)
       {
       }
   
       name_value_pair(const name_value_pair &) = default;
       name_value_pair(name_value_pair &&) = default;
       name_value_pair &operator=(const name_value_pair &) = default;
       name_value_pair &operator=(name_value_pair &&) = default;
   
       [[nodiscard]] const std::string &name() const { return m_name; }
       [[nodiscard]] T &value() const { return m_value; }
   
     private:
       std::string m_name;
       T &m_value;
   };
   
   template <typename T>
   class element_nvp : public name_value_pair<T>
   {
     public:
       element_nvp(std::string name, T &value)
           : name_value_pair<T>(std::move(name), value)
       {
       }
   };
   
   template <typename T>
   class attribute_nvp : public name_value_pair<T>
   {
     public:
       attribute_nvp(std::string name, T &value)
           : name_value_pair<T>(std::move(name), value)
       {
       }
   };
   
   template <typename T>
   constexpr attribute_nvp<T> make_attribute_nvp(std::string name, T &value)
   {
       return attribute_nvp(std::move(name), value);
   }
   
   template <typename T>
   constexpr element_nvp<T> make_element_nvp(std::string name, T &value)
   {
       return element_nvp(std::move(name), value);
   }
   
   
   
   struct serializer
   {
       explicit serializer(element_container &node)
           : m_node(node)
       {
       }
   
   
       template <typename T>
       serializer &operator&(const element_nvp<T> &rhs)
       {
           return serialize_element(rhs.name(), rhs.value());
       }
   
       template <typename T>
       serializer &operator&(const attribute_nvp<T> &rhs)
       {
           return serialize_attribute(rhs.name(), rhs.value());
       }
   
       template <typename T>
       serializer &serialize_element(const T &data);
   
       template <typename T>
       serializer &serialize_element(std::string_view name, const T &data);
   
       template <typename T>
       serializer &serialize_attribute(std::string_view name, const T &data);
   
     private:
       element_container &m_node;
   
   };
   
   
   struct deserializer
   {
       explicit deserializer(const element_container &node)
           : m_node(node)
       {
       }
   
   
       template <typename T>
       deserializer &operator&(const element_nvp<T> &rhs)
       {
           return deserialize_element(rhs.name(), rhs.value());
       }
   
       template <typename T>
       deserializer &operator&(const attribute_nvp<T> &rhs)
       {
           return deserialize_attribute(rhs.name(), rhs.value());
       }
   
       template <typename T>
       deserializer &deserialize_element(T &data);
   
       template <typename T>
       deserializer &deserialize_element(std::string_view name, T &data);
   
       template <typename T>
       deserializer &deserialize_attribute(std::string_view name, T &data);
   
     private:
       const element_container &m_node;
   
   };
   
   // --------------------------------------------------------------------
   
   
   template <typename T>
   struct type_serializer;
   
   
   template <typename T, size_t N>
   struct type_serializer<T[N]>
   {
       using value_type = std::remove_cvref_t<T>;
       using type_serializer_type = type_serializer<value_type>;
   
       static std::string type_name() { return type_serializer_type::type_name(); }
   
       static void serialize_child(element_container &n, std::string_view name, const value_type (&value)[N])
       {
           for (const value_type &v : value)
               type_serializer_type::serialize_child(n, name, v);
       }
   
       static void deserialize_child(const element_container &n, std::string_view name, value_type (&value)[N])
       {
           size_t ix = 0;
           for (auto &e : n)
           {
               if (e.name() != name)
                   continue;
   
               value_type v = {};
               type_serializer_type::deserialize_child(e, ".", v);
   
               value[ix] = std::move(v);
               ++ix;
   
               if (ix >= N)
                   break;
           }
       }
   };
   
   template <typename T>
       requires std::is_enum_v<T>
   struct type_serializer<T>
       : public value_serializer<T>
   {
       using value_type = T;
       using value_serializer_type = value_serializer<T>;
       using value_serializer_type::type_name;
   
       static std::string serialize_value(const T &value)
       {
           return value_serializer_type::to_string(value);
       }
   
       static T deserialize_value(std::string_view value)
       {
           return value_serializer_type::from_string(value);
       }
   
       static void serialize_child(element_container &n, std::string_view name, const value_type &value)
       {
           if (name.empty() or name == ".")
           {
               if (n.type() == node_type::element)
                   static_cast<element &>(n).set_content(value_serializer_type::to_string(value));
           }
           else
               n.emplace_back(name)->set_content(value_serializer_type::to_string(value));
       }
   
       static void deserialize_child(const element_container &n, std::string_view name, value_type &value)
       {
           value = value_type();
   
           if (name.empty() or name == ".")
           {
               if (n.type() == node_type::element)
                   value = value_serializer_type::from_string(static_cast<const element &>(n).get_content());
           }
           else
           {
               auto e = std::find_if(n.begin(), n.end(), [name](auto &e)
                   { return e.name() == name; });
               if (e != n.end())
                   value = value_serializer_type::from_string(e->get_content());
           }
       }
   };
   
   template <typename T>
       requires has_serialize_v<T, serializer>
   struct type_serializer<T>
   {
       using value_type = std::remove_cvref_t<T>;
   
       // the name of this type
       std::string m_type_name;
   
       static std::string type_name() { return instance().m_type_name.c_str(); }
       void type_name(std::string_view name) { m_type_name = name; }
   
       static type_serializer &instance()
       {
           static type_serializer s_instance{ std::source_location::current().function_name() };
           return s_instance;
       }
   
       static void serialize_child(element_container &n, std::string_view name, const value_type &value)
       {
           if (name.empty() or name == ".")
           {
               serializer sr(n);
               const_cast<value_type &>(value).serialize(sr, 0UL);
           }
           else
           {
               element *e = static_cast<element *>(n.emplace_back(name));
               serializer sr(*e);
               const_cast<value_type &>(value).serialize(sr, 0UL);
           }
       }
   
       static void deserialize_child(const element_container &n, std::string_view name, value_type &value)
       {
           value = value_type();
   
           if (name.empty() or name == ".")
           {
               deserializer sr(n);
               value.serialize(sr, 0UL);
           }
           else
           {
               auto e = std::find_if(n.begin(), n.end(), [name](auto &e)
                   { return e.name() == name; });
               if (e != n.end())
               {
                   deserializer sr(*e);
                   value.serialize(sr, 0UL);
               }
           }
       }
   };
   
   template <typename T>
   struct type_serializer<std::optional<T>>
   {
       using value_type = T;
       using container_type = std::optional<value_type>;
       using type_serializer_type = type_serializer<value_type>;
   
       static std::string type_name() { return type_serializer_type::type_name(); }
   
       static void serialize_child(element_container &n, std::string_view name, const container_type &value)
       {
           if (value.has_value())
               type_serializer_type::serialize_child(n, name, *value);
       }
   
       static void deserialize_child(const element_container &n, std::string_view name, container_type &value)
       {
           for (auto &e : n)
           {
               if (e.name() != name)
                   continue;
   
               value_type v = {};
               type_serializer_type::deserialize_child(e, ".", v);
               value.emplace(std::move(v));
           }
       }
   };
   
   // nice trick to enforce order in template selection
   template <unsigned N>
   struct priority_tag  : priority_tag<N - 1> 
   {
   };
   
   template <>
   struct priority_tag<0>
   {
   };
   
   template <typename T>
       requires is_serializable_array_type_v<T, serializer>
   struct type_serializer<T>
   {
       using container_type = std::remove_cvref_t<T>;
       using value_type = value_type_t<container_type>;
       using type_serializer_type = type_serializer<value_type>;
   
       static std::string type_name() { return type_serializer_type::type_name(); }
   
       static void serialize_child(element_container &n, std::string_view name, const container_type &value)
       {
           for (const value_type &v : value)
               type_serializer_type::serialize_child(n, name, v);
       }
   
       template <size_t N>
       static auto deserialize_array(const element_container &n, std::string_view name,
           std::array<value_type, N> &value, [[maybe_unused]] priority_tag<2> pt)
       {
           size_t ix = 0;
           for (auto &e : n)
           {
               if (e.name() != name)
                   continue;
   
               value_type v = {};
               type_serializer_type::deserialize_child(e, ".", v);
   
               value[ix] = std::move(v);
               ++ix;
   
               if (ix >= N)
                   break;
           }
       }
   
       template <typename A>
       static auto deserialize_array(const element_container &n, std::string_view name, A &arr, [[maybe_unused]] priority_tag<1> pt)
           -> decltype(arr.reserve(std::declval<typename container_type::size_type>()),
               void())
       {
           arr.reserve(n.size());
   
           for (auto &e : n)
           {
               if (e.name() != name)
                   continue;
   
               value_type v = {};
               type_serializer_type::deserialize_child(e, ".", v);
   
               arr.emplace_back(std::move(v));
           }
       }
   
       static void deserialize_array(const element_container &n, std::string_view name, container_type &arr, [[maybe_unused]] priority_tag<0> pt)
       {
           for (auto &e : n)
           {
               if (e.name() != name)
                   continue;
   
               value_type v = {};
               type_serializer_type::deserialize_child(e, ".", v);
   
               arr.emplace_back(std::move(v));
           }
       }
   
       static void deserialize_child(const element_container &n, std::string_view name, container_type &value)
       {
           type_serializer::deserialize_array(n, name, value, priority_tag<2>{});
       }
   };
   
   template <typename T>
   struct type_serializer
   {
       using value_type = std::remove_cvref_t<T>;
       using value_serializer_type = value_serializer<value_type>;
   
       static std::string type_name() { return value_serializer_type::type_name(); }
   
       static std::string serialize_value(const T &value)
       {
           return value_serializer_type::to_string(value);
       }
   
       static T deserialize_value(std::string_view value)
       {
           return value_serializer_type::from_string(value);
       }
   
       static void serialize_child(element_container &n, std::string_view name, const value_type &value)
       {
           if (name.empty() or name == ".")
           {
               if (n.type() == node_type::element)
                   static_cast<element &>(n).set_content(value_serializer_type::to_string(value));
           }
           else
               n.emplace_back(name)->set_content(value_serializer_type::to_string(value));
       }
   
       static void deserialize_child(const element_container &n, std::string_view name, value_type &value)
       {
           value = {};
   
           if (name.empty() or name == ".")
           {
               if (n.type() == node_type::element)
                   value = value_serializer_type::from_string(static_cast<const element &>(n).get_content());
           }
           else
           {
               auto e = std::find_if(n.begin(), n.end(), [name](auto &e)
                   { return e.name() == name; });
               if (e != n.end())
                   value = value_serializer_type::from_string(e->get_content());
           }
       }
   };
   
   // And finally, the implementation of serializer, deserializer and schema_creator.
   
   template <typename T>
   serializer &serializer::serialize_element(const T &value)
   {
       using value_type = std::remove_cvref_t<T>;
       using type_serializer = type_serializer<value_type>;
   
       type_serializer::serialize_child(m_node, "", value);
   
       return *this;
   }
   
   template <typename T>
   serializer &serializer::serialize_element(std::string_view name, const T &value)
   {
       using value_type = std::remove_cvref_t<T>;
       using type_serializer = type_serializer<value_type>;
   
       type_serializer::serialize_child(m_node, name, value);
   
       return *this;
   }
   
   template <typename T>
   serializer &serializer::serialize_attribute(std::string_view name, const T &value)
   {
       using value_type = std::remove_cvref_t<T>;
       using type_serializer = type_serializer<value_type>;
   
       if (m_node.type() == node_type::element)
           static_cast<element &>(m_node).attributes().emplace(name, type_serializer::serialize_value(value));
   
       return *this;
   }
   
   template <typename T>
   deserializer &deserializer::deserialize_element(T &value)
   {
       using value_type = std::remove_cvref_t<T>;
       using type_serializer = type_serializer<value_type>;
   
       type_serializer::deserialize_child(m_node, "", value);
   
       return *this;
   }
   
   template <typename T>
   deserializer &deserializer::deserialize_element(std::string_view name, T &value)
   {
       using value_type = std::remove_cvref_t<T>;
       using type_serializer = type_serializer<value_type>;
   
       type_serializer::deserialize_child(m_node, name, value);
   
       return *this;
   }
   
   template <typename T>
   deserializer &deserializer::deserialize_attribute(std::string_view name, T &value)
   {
       using value_type = std::remove_cvref_t<T>;
       using type_serializer = type_serializer<value_type>;
   
       if (m_node.type() == node_type::element)
       {
           std::string attr = static_cast<const element &>(m_node).get_attribute(name);
           if (not attr.empty())
               value = type_serializer::deserialize_value(attr);
       }
       return *this;
   }
   
   
   // --------------------------------------------------------------------
   // Convenience routines
   
   
   template <typename T>
   void to_xml(zeem::element_container &e, const T &value)
   {
       serializer sr(e);
       sr.serialize_element(value);
   }
   
   
   template <typename T>
   void to_xml(zeem::element_container &e, std::string_view name, const T &value)
   {
       serializer sr(e);
       sr.serialize_element(name, value);
   }
   
   
   template <typename T>
   void from_xml(const zeem::element_container &e, T &value)
   {
       deserializer dsr(e);
       dsr.deserialize_element(value);
   }
   
   
   template <typename T>
   void from_xml(const zeem::element_container &e, std::string_view name, T &value)
   {
       deserializer dsr(e);
       dsr.deserialize_element(name, value);
   }
   
   } // namespace zeem
