jsonizer.tpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. //
  2. // jsonizer.tpp
  3. // serializer
  4. //
  5. // Created by Sam Jaffe on 3/15/23.
  6. //
  7. #pragma once
  8. #include <fstream>
  9. #include <iostream>
  10. #include <stdexcept>
  11. #include <expect/expect.hpp>
  12. #include <json/json.h>
  13. #include <magic_enum.hpp>
  14. #include <string_utils/cast.h>
  15. #include <serializer/jsonizer.h>
  16. #include <serializer/shared_cache.h>
  17. #include <serializer/strconv.h>
  18. #include <serializer/traits.h>
  19. namespace serializer {
  20. Jsonizer::Jsonizer() : p_cache(std::make_shared<SharedCache>()) {}
  21. /**
  22. * @brief Construct a jsonizer with an externally owned cache
  23. * @param cache A shared_ptr to a data cache. Cannot be null
  24. * @throws std::logic_error if cache == nullptr
  25. */
  26. Jsonizer::Jsonizer(std::shared_ptr<SharedCache> cache) : p_cache(cache) {
  27. expects(p_cache != nullptr);
  28. }
  29. template <typename T, size_t... Is>
  30. Json::Value Jsonizer::to_json(T & value, std::index_sequence<Is...>) const {
  31. Json::Value json;
  32. [[maybe_unused]] auto l = {
  33. ((json[int(Is)] = to_json(std::get<Is>(value))), 0)...};
  34. return json;
  35. }
  36. template <typename T> Json::Value Jsonizer::to_json(T const & value) const {
  37. if constexpr (detail::has_serial_type_v<T>) {
  38. return to_json(static_cast<typename T::serial_type>(value));
  39. } else if constexpr (std::is_enum_v<T>) {
  40. constexpr auto type =
  41. magic_enum::as_flags<magic_enum::detail::is_flags_v<T>>;
  42. return std::string(magic_enum::enum_name<type>(value));
  43. } else if constexpr (detail::is_tuple_v<T>) {
  44. return to_json(value, std::make_index_sequence<std::tuple_size_v<T>>());
  45. } else if constexpr (std::is_constructible_v<std::string, T>) {
  46. return std::string(value);
  47. } else if constexpr (std::is_floating_point_v<T>) {
  48. return static_cast<double>(value);
  49. } else if constexpr (std::is_same_v<bool, T>) {
  50. return value;
  51. } else if constexpr (std::is_arithmetic_v<T>) {
  52. return static_cast<int>(value);
  53. } else if constexpr (detail::is_associative_container_v<T>) {
  54. Json::Value rval;
  55. using K = std::decay_t<typename T::key_type>;
  56. if constexpr (std::is_same_v<std::string, K> || std::is_arithmetic_v<K>) {
  57. for (auto const & [k, v] : value) {
  58. rval[to_string(k)] = to_json(v);
  59. }
  60. } else {
  61. for (auto const & pair : value) {
  62. rval.append(to_json(pair));
  63. }
  64. }
  65. return rval;
  66. } else if constexpr (detail::is_container_v<T>) {
  67. Json::Value rval;
  68. for (auto const & elem : value) {
  69. rval.append(to_json(elem));
  70. }
  71. return rval;
  72. } else {
  73. return to_json_impl(value);
  74. }
  75. }
  76. template <typename T, size_t... Is>
  77. void Jsonizer::from_json(T & value, Json::Value const & json,
  78. std::index_sequence<Is...>) const {
  79. [[maybe_unused]] auto l = {
  80. ((std::get<Is>(value) =
  81. from_json<std::tuple_element_t<Is, T>>(json[int(Is)])),
  82. 0)...};
  83. }
  84. template <typename T>
  85. void Jsonizer::from_json(T & value, Json::Value const & json) const {
  86. if (json.isNull()) return;
  87. if constexpr (detail::has_serial_type_v<T>) {
  88. value = T(from_json<typename T::serial_type>(json));
  89. } else if constexpr (std::is_enum_v<T>) {
  90. if (!string_utils::cast(value, json.asString())) {
  91. throw std::invalid_argument("Cannot cast to enum: " + json.asString());
  92. }
  93. } else if constexpr (std::is_same_v<std::string, T>) {
  94. value = json.asString();
  95. } else if constexpr (detail::is_tuple_v<T>) {
  96. from_json(value, json, std::make_index_sequence<std::tuple_size_v<T>>());
  97. } else if constexpr (detail::is_associative_container_v<T>) {
  98. using K = std::decay_t<typename T::key_type>;
  99. using V = std::decay_t<typename T::mapped_type>;
  100. if (json.isArray()) {
  101. std::vector<std::pair<K, V>> tmp;
  102. from_json(tmp, json);
  103. value.insert(tmp.begin(), tmp.end());
  104. } else if (json.isObject()) {
  105. if constexpr (std::is_constructible_v<K, std::string>) {
  106. for (auto it = json.begin(), end = json.end(); it != end; ++it) {
  107. value.emplace(it.key().asString(), from_json<V>(*it));
  108. }
  109. } else {
  110. for (auto it = json.begin(), end = json.end(); it != end; ++it) {
  111. value.emplace(from_string<K>(it.key().asString()), from_json<V>(*it));
  112. }
  113. }
  114. } else {
  115. throw std::invalid_argument(
  116. "cannot construct container from non-container");
  117. }
  118. } else if constexpr (detail::is_container_v<T>) {
  119. using V = std::decay_t<typename T::value_type>;
  120. if (!json.isArray()) {
  121. throw std::invalid_argument("cannot construct container from non-array");
  122. }
  123. for (auto const & elem : json) {
  124. value.insert(value.end(), from_json<V>(elem));
  125. }
  126. } else if constexpr (std::is_floating_point_v<T>) {
  127. value = static_cast<T>(json.asDouble());
  128. } else if constexpr (std::is_same_v<bool, T>) {
  129. value = json.asBool();
  130. } else if constexpr (std::is_arithmetic_v<T>) {
  131. value = static_cast<T>(json.asInt());
  132. } else {
  133. from_json_impl(value, json);
  134. }
  135. }
  136. template <typename T, typename F>
  137. T Jsonizer::from_cached_json(std::string const & key, Json::Value const & json,
  138. F && fetch) const {
  139. std::shared_ptr<T const> ptr = p_cache->fetch<T>(json[key].asString());
  140. if (!ptr) {
  141. T value{};
  142. std::forward<F>(fetch)(value, json);
  143. ptr = p_cache->store(value.name, std::move(value));
  144. }
  145. return *ptr;
  146. }
  147. template <typename T>
  148. void Jsonizer::from_json(std::shared_ptr<T const> & ptr,
  149. Json::Value const & json) const {
  150. if (json.isString()) {
  151. ptr = p_cache->fetch<T>(json.asString());
  152. } else {
  153. T local{};
  154. from_json(local, json);
  155. ptr = p_cache->store(local.name, local);
  156. }
  157. }
  158. template <typename T> T Jsonizer::from_json(Json::Value const & json) const {
  159. std::decay_t<T> tmp;
  160. from_json(tmp, json);
  161. return tmp;
  162. }
  163. template <typename T> T Jsonizer::from_stream(std::istream & in) const {
  164. Json::Value root;
  165. in >> root;
  166. return from_json<T>(root);
  167. }
  168. template <typename T> T Jsonizer::from_string(std::string const & in) const {
  169. std::stringstream ss;
  170. ss << in;
  171. return from_stream<T>(ss);
  172. }
  173. template <typename T>
  174. void Jsonizer::to_stream(T const & value, std::ostream & out) const {
  175. Json::Value root = to_json(value);
  176. out << root;
  177. }
  178. template <typename T> T Jsonizer::from_file(std::string const & file) const {
  179. std::ifstream in(file);
  180. return from_stream<T>(in);
  181. }
  182. }