// // jsonizer.tpp // serializer // // Created by Sam Jaffe on 3/15/23. // #pragma once #include #include #include #include #include #include #include #include #include #include namespace serializer { inline Jsonizer::Jsonizer() : p_cache(std::make_shared()) {} /** * @brief Construct a jsonizer with an externally owned cache * @param cache A shared_ptr to a data cache. Cannot be null * @throws std::logic_error if cache == nullptr */ inline Jsonizer::Jsonizer(std::shared_ptr cache) : p_cache(cache) { expects(p_cache != nullptr); } template Json::Value Jsonizer::to_json(T & value, std::index_sequence) const { Json::Value json; [[maybe_unused]] auto l = { ((json[int(Is)] = to_json(std::get(value))), 0)...}; return json; } template Json::Value Jsonizer::to_json(std::optional const &opt) const { return opt.has_value() ? to_json(*opt) : Json::Value(); } template Json::Value Jsonizer::to_json(T const & value) const { if constexpr (detail::has_serial_type_v) { return to_json(static_cast(value)); } else if constexpr (std::is_enum_v) { constexpr auto type = magic_enum::as_flags>; return std::string(magic_enum::enum_name(value)); } else if constexpr (detail::is_tuple_v) { return to_json(value, std::make_index_sequence>()); } else if constexpr (std::is_constructible_v) { return std::string(value); } else if constexpr (std::is_floating_point_v) { return static_cast(value); } else if constexpr (std::is_same_v) { return value; } else if constexpr (std::is_arithmetic_v && std::is_unsigned_v) { return static_cast(value); } else if constexpr (std::is_arithmetic_v) { return static_cast(value); } else if constexpr (detail::is_associative_container_v) { Json::Value rval; using K = std::decay_t; if constexpr (std::is_same_v || std::is_arithmetic_v) { for (auto const & [k, v] : value) { rval[to_string(k)] = to_json(v); } } else { for (auto const & pair : value) { rval.append(to_json(pair)); } } return rval; } else if constexpr (detail::is_container_v) { Json::Value rval; for (auto const & elem : value) { rval.append(to_json(elem)); } return rval; } else { return to_json_impl(value); } } template void Jsonizer::from_json(T & value, Json::Value const & json, std::index_sequence) const { [[maybe_unused]] auto l = { ((std::get(value) = from_json>(json[int(Is)])), 0)...}; } template void Jsonizer::from_json(std::optional &opt, Json::Value const & json) const { opt = json.isNull() ? std::nullopt : std::optional{from_json(json)}; } template void Jsonizer::from_json(T & value, Json::Value const & json) const { if (json.isNull()) return; if constexpr (detail::has_serial_type_v) { value = T(from_json(json)); } else if constexpr (std::is_enum_v) { if (json.isInt()) { value = *magic_enum::enum_cast(json.asInt()); } else if (!string_utils::cast(value, json.asString())) { throw std::invalid_argument("Cannot cast to enum: " + json.asString()); } } else if constexpr (std::is_same_v) { value = json.asString(); } else if constexpr (detail::is_tuple_v) { from_json(value, json, std::make_index_sequence>()); } else if constexpr (detail::is_associative_container_v) { using K = std::decay_t; using V = std::decay_t; if (json.isArray()) { std::vector> tmp; from_json(tmp, json); value.insert(tmp.begin(), tmp.end()); } else if (json.isObject()) { if constexpr (std::is_constructible_v) { for (auto it = json.begin(), end = json.end(); it != end; ++it) { value.emplace(it.key().asString(), from_json(*it)); } } else { for (auto it = json.begin(), end = json.end(); it != end; ++it) { value.emplace(from_string(it.key().asString()), from_json(*it)); } } } else { throw std::invalid_argument( "cannot construct container from non-container"); } } else if constexpr (detail::is_container_v) { using V = std::decay_t; if (!json.isArray()) { throw std::invalid_argument("cannot construct container from non-array"); } for (auto const & elem : json) { value.insert(value.end(), from_json(elem)); } } else if constexpr (std::is_floating_point_v) { value = static_cast(json.asDouble()); } else if constexpr (std::is_same_v) { value = json.asBool(); } else if constexpr (std::is_arithmetic_v && std::is_unsigned_v) { value = static_cast(json.asUInt64()); } else if constexpr (std::is_arithmetic_v) { value = static_cast(json.asInt64()); } else { from_json_impl(value, json); } } template T Jsonizer::from_cached_json(std::string const & key, Json::Value const & json, F && fetch) const { std::shared_ptr ptr = p_cache->fetch(json[key].asString()); if (!ptr) { T value{}; std::forward(fetch)(value, json); ptr = p_cache->store(value.name, std::move(value)); } return *ptr; } template void Jsonizer::from_json(std::shared_ptr & ptr, Json::Value const & json) const { if (json.isString()) { ptr = p_cache->fetch(json.asString()); } else { T local{}; from_json(local, json); ptr = p_cache->store(local.name, local); } } }