// // jsonizer.tpp // serializer // // Created by Sam Jaffe on 3/15/23. // #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace serializer { 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 */ 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(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) { 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(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 (!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) { value = static_cast(json.asInt()); } else { from_json_impl(value, json); } } template T Jsonizer::from_json(Json::Value const & json) const { std::decay_t tmp; from_json(tmp, json); return tmp; } template T Jsonizer::from_stream(std::istream & in) const { Json::Value root; in >> root; return from_json(root); } template T Jsonizer::from_string(std::string const & in) const { std::stringstream ss; ss << in; return from_stream(ss); } template void Jsonizer::to_stream(T const & value, std::ostream & out) const { Json::Value root = to_json(value); out << root; } template T Jsonizer::from_file(std::string const & file) const { std::ifstream in(file); return from_stream(in); } }