Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
JsonSerializationAdapter.h
Go to the documentation of this file.
1#pragma once
2
4
5#include <boost/pfr.hpp>
6#include <ostream>
7#include <variant>
10
11namespace storm {
14
15template<typename EnumDecl>
16 requires std::is_enum_v<typename EnumDecl::Values>
18 public:
19 using EnumDeclaration = EnumDecl;
20 using E = typename EnumDeclaration::Values;
21 auto static constexpr Keys = EnumDeclaration::Keys;
22 E static constexpr Uninitialized = static_cast<E>(std::numeric_limits<std::underlying_type_t<E>>::max());
23
24 SerializedEnum() = default;
25 SerializedEnum(E value) : value(value) {}
26 SerializedEnum(std::string_view str) {
27 auto findRes = std::find(std::begin(Keys), std::end(Keys), str);
28 STORM_LOG_THROW(findRes != std::end(Keys), storm::exceptions::WrongFormatException, "Invalid enum value '" << str << "'.");
29 value = static_cast<E>(std::distance(std::begin(Keys), findRes));
30 }
31
32 bool isInitialized() const {
33 return value != Uninitialized;
34 }
35
36 operator E() const {
37 STORM_LOG_ASSERT(isInitialized(), "Enum value not initialized.");
38 return value;
39 }
40
41 bool operator==(E other) const {
42 return value == other;
43 }
44
45 std::string_view toString() const {
46 auto const index = static_cast<std::underlying_type_t<E>>(value);
47 STORM_LOG_ASSERT(isInitialized(), "Enum value not initialized.");
48 STORM_LOG_ASSERT(std::cmp_less(index, Keys.size()), "Enum value with index " << index << " does not have a key.");
49 return *(std::begin(Keys) + index);
50 }
51
52 friend std::ostream& operator<<(std::ostream& os, SerializedEnum const& val) {
53 os << val.toString();
54 return os;
55 }
56
57 private:
58 E value{Uninitialized};
59};
60} // namespace storm
61
62NLOHMANN_JSON_NAMESPACE_BEGIN
63
64template<typename T>
65concept StormEnableSerializationConcept = std::is_same_v<typename T::JsonSerialization, ::storm::JsonSerialization>;
66
67template<typename T>
68concept StormIsOptional = std::same_as<T, std::optional<typename T::value_type>>;
69
70template<StormEnableSerializationConcept T>
71struct adl_serializer<T> {
72 static_assert(T::JsonKeys.size() == boost::pfr::tuple_size_v<T>, "Number of JsonKeys does not match number of fields in struct.");
73
74 template<typename JsonType>
75 static void to_json(JsonType& json, T const& val) {
76 to_json_i<0>(json, val);
77 if (json.is_null()) {
78 json = typename JsonType::object_t();
79 }
80 }
81
82 template<typename JsonType>
83 static void from_json(JsonType const& json, T& val) {
84 STORM_LOG_THROW(json.is_object(), ::storm::exceptions::WrongFormatException, "Expected an object, got something else.");
85 from_json_i<0>(json, val);
86 // TODO: check if additional fields are present
87 }
88
89 private:
90 static auto get_key(std::size_t i) {
91 return std::data(T::JsonKeys)[i];
92 }
93
94 template<std::size_t I, typename JsonType>
95 static void to_json_i(JsonType& json, T const& val) {
96 if constexpr (I < boost::pfr::tuple_size_v<T>) {
97 if constexpr (StormIsOptional<boost::pfr::tuple_element_t<I, T>>) {
98 if (boost::pfr::get<I>(val).has_value()) {
99 json[get_key(I)] = boost::pfr::get<I>(val).value();
100 }
101 } else {
102 json[get_key(I)] = boost::pfr::get<I>(val);
103 }
104 to_json_i<I + 1>(json, val);
105 }
106 }
107
108 template<std::size_t I, typename JsonType>
109 static void from_json_i(JsonType const& json, T& val) {
110 if constexpr (I < boost::pfr::tuple_size_v<T>) {
111 if constexpr (StormIsOptional<boost::pfr::tuple_element_t<I, T>>) {
112 if (json.contains(get_key(I))) {
113 boost::pfr::get<I>(val) = json.at(get_key(I)).template get<typename boost::pfr::tuple_element_t<I, T>::value_type>();
114 } else {
115 boost::pfr::get<I>(val) = std::nullopt;
116 }
117 } else {
118 json.at(get_key(I)).get_to(boost::pfr::get<I>(val));
119 }
120 from_json_i<I + 1>(json, val);
121 }
122 }
123};
124
125template<typename T>
126concept StormIsSerializedEnum = std::same_as<T, ::storm::SerializedEnum<typename T::EnumDeclaration>>;
127
128template<StormIsSerializedEnum T>
129struct adl_serializer<T> {
130 template<typename JsonType>
131 static void to_json(JsonType& json, T const& val) {
132 json = val.toString();
133 }
134
135 template<typename JsonType>
136 static void from_json(JsonType const& json, T& val) {
137 STORM_LOG_THROW(json.is_string(), ::storm::exceptions::WrongFormatException, "Expected a string, got something else.");
138 val = T(json.template get<std::string>());
139 }
140};
141
142template<typename... Ts>
143struct adl_serializer<std::variant<Ts...>> {
144 template<typename JsonType>
145 static void to_json(JsonType& json, std::variant<Ts...> const& val) {
146 std::visit([&json](auto const& v) { adl_serializer<std::decay_t<decltype(v)>>::to_json(json, v); }, val);
147 }
148
149 template<std::size_t I, typename JsonType>
150 static void from_json_i(JsonType const& json, std::variant<Ts...>& val) {
151 static_assert(I < std::variant_size_v<std::variant<Ts...>>, "Index out of bounds.");
152 using Ti = std::variant_alternative_t<I, std::variant<Ts...>>;
153 if constexpr (I + 1 == std::variant_size_v<std::variant<Ts...>>) {
154 // Last alternative, try to deserialize directly (potentially throwing some error if it fails)
155 val = json.template get<Ti>();
156 } else {
157 // There are more alternatives left, so check if the current type matches
158 if (json.is_object()) {
160 if (std::all_of(json.items().begin(), json.items().end(),
161 [](auto const& item) { return std::find(Ti::JsonKeys.begin(), Ti::JsonKeys.end(), item.key()) != Ti::JsonKeys.end(); })) {
162 // All keys in the json can be found in the struct
163 val = json.template get<Ti>();
164 return;
165 }
166 }
167 } else if ((json.is_boolean() && std::is_same_v<Ti, bool>) || (json.is_string() && std::is_same_v<Ti, std::string>) ||
168 (json.is_number_integer() && std::is_integral_v<Ti>) ||
169 (json.is_number_float() && (std::is_same_v<Ti, double> || std::is_same_v<Ti, typename JsonType::number_float_t>))) {
170 // Type matches
171 val = json.template get<Ti>();
172 return;
173 }
174 // If we reach this point, we try the next alternative
175 from_json_i<I + 1>(json, val);
176 }
177 }
178
179 template<typename JsonType>
180 static void from_json(JsonType const& json, std::variant<Ts...>& val) {
181 from_json_i<0>(json, val);
182 }
183};
184
185NLOHMANN_JSON_NAMESPACE_END
std::string_view toString() const
typename EnumDeclaration::Values E
SerializedEnum(std::string_view str)
friend std::ostream & operator<<(std::ostream &os, SerializedEnum const &val)
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
nlohmann::basic_json< std::map, std::vector, std::string, bool, int64_t, uint64_t, ValueType > json
Definition JsonForward.h:10
static void from_json(JsonType const &json, T &val)
static void to_json(JsonType &json, T const &val)
static void to_json(JsonType &json, std::variant< Ts... > const &val)
static void from_json_i(JsonType const &json, std::variant< Ts... > &val)
static void from_json(JsonType const &json, std::variant< Ts... > &val)
Helper struct to enable json serialization.