4
4
#include < sourcemeta/core/json_value.h>
5
5
6
6
#include < algorithm> // std::sort
7
+ #include < cassert> // assert
7
8
#include < concepts> // std::same_as, std::constructible_from
8
9
#include < functional> // std::function
9
10
#include < optional> // std::optional
10
11
#include < tuple> // std::tuple, std::apply, std::tuple_element_t, std::tuple_size, std::tuple_size_v
11
12
#include < type_traits> // std::false_type, std::true_type, std::void_t, std::is_enum_v, std::underlying_type_t, std::is_same_v, std::is_base_of_v, std::remove_cvref_t
12
- #include < utility> // std::pair
13
+ #include < utility> // std::pair, std:::make_index_sequence, std::index_sequence
13
14
14
15
// Forward declarations (added as needed)
15
16
#ifndef DOXYGEN
@@ -36,7 +37,13 @@ struct json_auto_is_basic_string<std::basic_string<CharT, Traits, Alloc>>
36
37
37
38
// / @ingroup json
38
39
template <typename T>
39
- concept json_auto_has_method = requires (const T value) {
40
+ concept json_auto_has_method_from = requires (const JSON &value) {
41
+ { T::from_json (value) } -> std::same_as<T>;
42
+ };
43
+
44
+ // / @ingroup json
45
+ template <typename T>
46
+ concept json_auto_has_method_to = requires (const T value) {
40
47
{ value.to_json () } -> std::same_as<JSON>;
41
48
};
42
49
@@ -61,7 +68,8 @@ concept json_auto_list_like =
61
68
{ type.cbegin () } -> std::same_as<typename T::const_iterator>;
62
69
{ type.cend () } -> std::same_as<typename T::const_iterator>;
63
70
} && json_auto_supports_auto<T> && !json_auto_has_mapped_type<T>::value &&
64
- !json_auto_has_method<T> && !json_auto_is_basic_string<T>::value;
71
+ !json_auto_has_method_from<T> && !json_auto_has_method_to<T> &&
72
+ !json_auto_is_basic_string<T>::value;
65
73
66
74
// / @ingroup json
67
75
template <typename T>
@@ -73,7 +81,7 @@ concept json_auto_map_like =
73
81
{ type.cbegin () } -> std::same_as<typename T::const_iterator>;
74
82
{ type.cend () } -> std::same_as<typename T::const_iterator>;
75
83
} && json_auto_supports_auto<T> && json_auto_has_mapped_type<T>::value &&
76
- !json_auto_has_method <T> &&
84
+ !json_auto_has_method_from<T> && !json_auto_has_method_to <T> &&
77
85
std::is_same_v<typename T::key_type, JSON::String>;
78
86
79
87
// / @ingroup json
@@ -86,6 +94,13 @@ struct json_auto_has_reverse_iterator<T,
86
94
std::void_t <typename T::reverse_iterator>>
87
95
: std::true_type {};
88
96
97
+ // / @ingroup json
98
+ template <typename T> struct json_auto_is_pair : std::false_type {};
99
+
100
+ // / @ingroup json
101
+ template <typename U, typename V>
102
+ struct json_auto_is_pair <std::pair<U, V>> : std::true_type {};
103
+
89
104
// / @ingroup json
90
105
template <typename T>
91
106
concept json_auto_tuple_mono = requires {
@@ -107,11 +122,35 @@ concept json_auto_tuple_poly =
107
122
// / @ingroup json
108
123
// / If the value has a `.to_json()` method, always prefer that
109
124
template <typename T>
110
- requires (json_auto_has_method <T>)
125
+ requires (json_auto_has_method_to <T>)
111
126
auto to_json (const T &value) -> JSON {
112
127
return value.to_json ();
113
128
}
114
129
130
+ // / @ingroup json
131
+ // / If the value has a `.from_json()` static method, always prefer that
132
+ template <typename T>
133
+ requires (json_auto_has_method_from<T>)
134
+ auto from_json (const JSON &value) -> T {
135
+ return T::from_json (value);
136
+ }
137
+
138
+ // / @ingroup json
139
+ template <typename T>
140
+ requires std::is_same_v<T, bool >
141
+ auto from_json (const JSON &value) -> T {
142
+ assert (value.is_boolean ());
143
+ return value.to_boolean ();
144
+ }
145
+
146
+ // / @ingroup json
147
+ template <typename T>
148
+ requires (std::is_integral_v<T> && !std::is_same_v<T, bool >)
149
+ auto from_json (const JSON &value) -> T {
150
+ assert (value.is_integer ());
151
+ return static_cast <T>(value.to_integer ());
152
+ }
153
+
115
154
// TODO: How can we keep this in the hash header that does not yet know about
116
155
// JSON?
117
156
// / @ingroup json
@@ -133,25 +172,83 @@ auto to_json(const T &hash) -> JSON {
133
172
return result;
134
173
}
135
174
175
+ // TODO: How can we keep this in the hash header that does not yet know about
176
+ // JSON?
177
+ // / @ingroup json
178
+ template <typename T>
179
+ requires std::is_same_v<T, JSON::Object::Container::hash_type>
180
+ auto from_json (const JSON &value) -> T {
181
+ assert (value.is_array ());
182
+ assert (value.size () == 4 );
183
+ #if defined(__SIZEOF_INT128__)
184
+ return {
185
+ (static_cast <__uint128_t >(from_json<std::uint64_t >(value.at (0 ))) << 64 ) |
186
+ from_json<std::uint64_t >(value.at (1 )),
187
+ (static_cast <__uint128_t >(from_json<std::uint64_t >(value.at (2 ))) << 64 ) |
188
+ from_json<std::uint64_t >(value.at (3 ))};
189
+ #else
190
+ return {from_json<std::uint64_t >(value.at (0 )),
191
+ from_json<std::uint64_t >(value.at (1 )),
192
+ from_json<std::uint64_t >(value.at (2 )),
193
+ from_json<std::uint64_t >(value.at (3 ))};
194
+ #endif
195
+ }
196
+
136
197
// / @ingroup json
137
198
template <typename T>
138
199
requires std::constructible_from<JSON, T>
139
200
auto to_json (const T &value) -> JSON {
140
201
return JSON{value};
141
202
}
142
203
204
+ // / @ingroup json
205
+ template <typename T>
206
+ requires std::is_same_v<T, JSON>
207
+ auto from_json (const JSON &value) -> T {
208
+ return value;
209
+ }
210
+
211
+ // / @ingroup json
212
+ template <typename T>
213
+ requires json_auto_is_basic_string<T>::value
214
+ auto from_json (const JSON &value) -> T {
215
+ assert (value.is_string ());
216
+ return value.to_string ();
217
+ }
218
+
143
219
// / @ingroup json
144
220
template <typename T>
145
221
requires std::is_enum_v<T>
146
222
auto to_json (const T value) -> JSON {
147
223
return to_json (static_cast <std::underlying_type_t <T>>(value));
148
224
}
149
225
226
+ // / @ingroup json
227
+ template <typename T>
228
+ requires std::is_enum_v<T>
229
+ auto from_json (const JSON &value) -> T {
230
+ assert (value.is_integer ());
231
+ assert (value.is_positive ());
232
+ return static_cast <T>(value.to_integer ());
233
+ }
234
+
150
235
// / @ingroup json
151
236
template <typename T> auto to_json (const std::optional<T> &value) -> JSON {
152
237
return value.has_value () ? to_json (value.value ()) : JSON{nullptr };
153
238
}
154
239
240
+ // / @ingroup json
241
+ template <typename T>
242
+ requires requires { typename T::value_type; } &&
243
+ std::is_same_v<T, std::optional<typename T::value_type>>
244
+ auto from_json (const JSON &value) -> T {
245
+ if (value.is_null ()) {
246
+ return {};
247
+ } else {
248
+ return from_json<typename T::value_type>(value);
249
+ }
250
+ }
251
+
155
252
// / @ingroup json
156
253
template <json_auto_list_like T>
157
254
auto to_json (typename T::const_iterator begin, typename T::const_iterator end)
@@ -204,6 +301,52 @@ auto to_json(
204
301
return to_json<T>(value.cbegin (), value.cend (), callback);
205
302
}
206
303
304
+ // / @ingroup json
305
+ template <json_auto_list_like T> auto from_json (const JSON &value) -> T {
306
+ assert (value.is_array ());
307
+ T result;
308
+
309
+ if constexpr (requires { result.reserve (value.size ()); }) {
310
+ result.reserve (value.size ());
311
+ }
312
+
313
+ for (const auto &item : value.as_array ()) {
314
+ if constexpr (requires {
315
+ result.insert (from_json<typename T::value_type>(item));
316
+ }) {
317
+ result.insert (from_json<typename T::value_type>(item));
318
+ } else {
319
+ result.push_back (from_json<typename T::value_type>(item));
320
+ }
321
+ }
322
+
323
+ return result;
324
+ }
325
+
326
+ template <json_auto_list_like T>
327
+ auto from_json (
328
+ const JSON &value,
329
+ const std::function<typename T::value_type (const JSON &)> &callback) -> T {
330
+ assert (value.is_array ());
331
+ T result;
332
+
333
+ if constexpr (requires { result.reserve (value.size ()); }) {
334
+ result.reserve (value.size ());
335
+ }
336
+
337
+ for (const auto &item : value.as_array ()) {
338
+ if constexpr (requires {
339
+ result.insert (from_json<typename T::value_type>(item));
340
+ }) {
341
+ result.insert (callback (item));
342
+ } else {
343
+ result.push_back (callback (item));
344
+ }
345
+ }
346
+
347
+ return result;
348
+ }
349
+
207
350
// / @ingroup json
208
351
template <json_auto_map_like T>
209
352
auto to_json (typename T::const_iterator begin, typename T::const_iterator end)
@@ -235,6 +378,31 @@ auto to_json(
235
378
return result;
236
379
}
237
380
381
+ // / @ingroup json
382
+ template <json_auto_map_like T> auto from_json (const JSON &value) -> T {
383
+ assert (value.is_object ());
384
+ T result;
385
+ for (const auto &item : value.as_object ()) {
386
+ result.emplace (item.first , from_json<typename T::mapped_type>(item.second ));
387
+ }
388
+
389
+ return result;
390
+ }
391
+
392
+ // / @ingroup json
393
+ template <json_auto_map_like T>
394
+ auto from_json (
395
+ const JSON &value,
396
+ const std::function<typename T::mapped_type (const JSON &)> &callback) -> T {
397
+ assert (value.is_object ());
398
+ T result;
399
+ for (const auto &item : value.as_object ()) {
400
+ result.emplace (item.first , callback (item.second ));
401
+ }
402
+
403
+ return result;
404
+ }
405
+
238
406
// / @ingroup json
239
407
template <json_auto_map_like T>
240
408
auto to_json (
@@ -253,6 +421,17 @@ auto to_json(const std::pair<L, R> &value) -> JSON {
253
421
return tuple;
254
422
}
255
423
424
+ // / @ingroup json
425
+ template <typename T>
426
+ requires json_auto_is_pair<T>::value
427
+ auto from_json (const JSON &value) -> T {
428
+ assert (value.is_array ());
429
+ assert (value.size () == 2 );
430
+ return std::make_pair<typename T::first_type, typename T::second_type>(
431
+ from_json<typename T::first_type>(value.at (0 )),
432
+ from_json<typename T::second_type>(value.at (1 )));
433
+ }
434
+
256
435
// Handle 1-element tuples
257
436
// / @ingroup json
258
437
template <json_auto_tuple_mono T> auto to_json (const T &value) -> JSON {
@@ -262,6 +441,13 @@ template <json_auto_tuple_mono T> auto to_json(const T &value) -> JSON {
262
441
return tuple;
263
442
}
264
443
444
+ // / @ingroup json
445
+ template <json_auto_tuple_mono T> auto from_json (const JSON &value) -> T {
446
+ assert (value.is_array ());
447
+ assert (value.size () == 1 );
448
+ return {from_json<std::tuple_element_t <0 , T>>(value.at (0 ))};
449
+ }
450
+
265
451
// / @ingroup json
266
452
template <json_auto_tuple_poly T> auto to_json (const T &value) -> JSON {
267
453
auto tuple = JSON::make_array ();
@@ -273,6 +459,22 @@ template <json_auto_tuple_poly T> auto to_json(const T &value) -> JSON {
273
459
return tuple;
274
460
}
275
461
462
+ #ifndef DOXYGEN
463
+ template <typename T, std::size_t ... Indices>
464
+ auto from_json_tuple_poly (const JSON &value, std::index_sequence<Indices...>)
465
+ -> T {
466
+ return {from_json<std::tuple_element_t <Indices, T>>(value.at (Indices))...};
467
+ }
468
+ #endif
469
+
470
+ // / @ingroup json
471
+ template <json_auto_tuple_poly T> auto from_json (const JSON &value) -> T {
472
+ assert (value.is_array ());
473
+ assert (value.size () == std::tuple_size_v<T>);
474
+ return from_json_tuple_poly<T>(
475
+ value, std::make_index_sequence<std::tuple_size_v<T>>{});
476
+ }
477
+
276
478
} // namespace sourcemeta::core
277
479
278
480
#endif
0 commit comments