| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- //
- // jsonizer.tpp
- // serializer
- //
- // Created by Sam Jaffe on 3/15/23.
- //
- #pragma once
- #include <stdexcept>
- #include <expect/expect.hpp>
- #include <json/json.h>
- #include <magic_enum.hpp>
- #include <string_utils/cast.h>
- #include <serializer/jsonizer.h>
- #include <serializer/jsonizer_ios.tpp>
- #include <serializer/shared_cache.h>
- #include <serializer/strconv.h>
- #include <serializer/traits.h>
- namespace serializer {
- inline Jsonizer::Jsonizer() : p_cache(std::make_shared<SharedCache>()) {}
- /**
- * @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<SharedCache> cache) : p_cache(cache) {
- expects(p_cache != nullptr);
- }
- template <typename T, size_t... Is>
- Json::Value Jsonizer::to_json(T & value, std::index_sequence<Is...>) const {
- Json::Value json;
- [[maybe_unused]] auto l = {
- ((json[int(Is)] = to_json(std::get<Is>(value))), 0)...};
- return json;
- }
- template <typename T> Json::Value Jsonizer::to_json(std::optional<T> const &opt) const {
- return opt.has_value() ? to_json(*opt) : Json::Value();
- }
- template <typename T> Json::Value Jsonizer::to_json(T const & value) const {
- if constexpr (detail::has_serial_type_v<T>) {
- return to_json(static_cast<typename T::serial_type>(value));
- } else if constexpr (std::is_enum_v<T>) {
- constexpr auto type =
- magic_enum::as_flags<magic_enum::detail::is_flags_v<T>>;
- return std::string(magic_enum::enum_name<type>(value));
- } else if constexpr (detail::is_tuple_v<T>) {
- return to_json(value, std::make_index_sequence<std::tuple_size_v<T>>());
- } else if constexpr (std::is_constructible_v<std::string, T>) {
- return std::string(value);
- } else if constexpr (std::is_floating_point_v<T>) {
- return static_cast<double>(value);
- } else if constexpr (std::is_same_v<bool, T>) {
- return value;
- } else if constexpr (std::is_arithmetic_v<T>) {
- return static_cast<int>(value);
- } else if constexpr (detail::is_associative_container_v<T>) {
- Json::Value rval;
- using K = std::decay_t<typename T::key_type>;
- if constexpr (std::is_same_v<std::string, K> || std::is_arithmetic_v<K>) {
- 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<T>) {
- Json::Value rval;
- for (auto const & elem : value) {
- rval.append(to_json(elem));
- }
- return rval;
- } else {
- return to_json_impl(value);
- }
- }
- template <typename T, size_t... Is>
- void Jsonizer::from_json(T & value, Json::Value const & json,
- std::index_sequence<Is...>) const {
- [[maybe_unused]] auto l = {
- ((std::get<Is>(value) =
- from_json<std::tuple_element_t<Is, T>>(json[int(Is)])),
- 0)...};
- }
- template <typename T>
- void Jsonizer::from_json(std::optional<T> &opt, Json::Value const & json) const {
- opt = json.isNull() ? std::nullopt : std::optional{from_json<T>(json)};
- }
- template <typename T>
- void Jsonizer::from_json(T & value, Json::Value const & json) const {
- if (json.isNull()) return;
- if constexpr (detail::has_serial_type_v<T>) {
- value = T(from_json<typename T::serial_type>(json));
- } else if constexpr (std::is_enum_v<T>) {
- if (!string_utils::cast(value, json.asString())) {
- throw std::invalid_argument("Cannot cast to enum: " + json.asString());
- }
- } else if constexpr (std::is_same_v<std::string, T>) {
- value = json.asString();
- } else if constexpr (detail::is_tuple_v<T>) {
- from_json(value, json, std::make_index_sequence<std::tuple_size_v<T>>());
- } else if constexpr (detail::is_associative_container_v<T>) {
- using K = std::decay_t<typename T::key_type>;
- using V = std::decay_t<typename T::mapped_type>;
- if (json.isArray()) {
- std::vector<std::pair<K, V>> tmp;
- from_json(tmp, json);
- value.insert(tmp.begin(), tmp.end());
- } else if (json.isObject()) {
- if constexpr (std::is_constructible_v<K, std::string>) {
- for (auto it = json.begin(), end = json.end(); it != end; ++it) {
- value.emplace(it.key().asString(), from_json<V>(*it));
- }
- } else {
- for (auto it = json.begin(), end = json.end(); it != end; ++it) {
- value.emplace(from_string<K>(it.key().asString()), from_json<V>(*it));
- }
- }
- } else {
- throw std::invalid_argument(
- "cannot construct container from non-container");
- }
- } else if constexpr (detail::is_container_v<T>) {
- using V = std::decay_t<typename T::value_type>;
- if (!json.isArray()) {
- throw std::invalid_argument("cannot construct container from non-array");
- }
- for (auto const & elem : json) {
- value.insert(value.end(), from_json<V>(elem));
- }
- } else if constexpr (std::is_floating_point_v<T>) {
- value = static_cast<T>(json.asDouble());
- } else if constexpr (std::is_same_v<bool, T>) {
- value = json.asBool();
- } else if constexpr (std::is_arithmetic_v<T>) {
- value = static_cast<T>(json.asInt());
- } else {
- from_json_impl(value, json);
- }
- }
- template <typename T, typename F>
- T Jsonizer::from_cached_json(std::string const & key, Json::Value const & json,
- F && fetch) const {
- std::shared_ptr<T const> ptr = p_cache->fetch<T>(json[key].asString());
- if (!ptr) {
- T value{};
- std::forward<F>(fetch)(value, json);
- ptr = p_cache->store(value.name, std::move(value));
- }
- return *ptr;
- }
- template <typename T>
- void Jsonizer::from_json(std::shared_ptr<T const> & ptr,
- Json::Value const & json) const {
- if (json.isString()) {
- ptr = p_cache->fetch<T>(json.asString());
- } else {
- T local{};
- from_json(local, json);
- ptr = p_cache->store(local.name, local);
- }
- }
- }
|