7
7
#include < cassert> // assert
8
8
#include < concepts> // std::same_as, std::constructible_from
9
9
#include < functional> // std::function
10
- #include < optional> // std::optional
10
+ #include < optional> // std::optional, std::nullopt, std::bad_optional_access
11
11
#include < tuple> // std::tuple, std::apply, std::tuple_element_t, std::tuple_size, std::tuple_size_v
12
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
13
13
#include < utility> // std::pair, std:::make_index_sequence, std::index_sequence
@@ -38,7 +38,7 @@ struct json_auto_is_basic_string<std::basic_string<CharT, Traits, Alloc>>
38
38
// / @ingroup json
39
39
template <typename T>
40
40
concept json_auto_has_method_from = requires (const JSON &value) {
41
- { T::from_json (value) } -> std::same_as<T >;
41
+ { T::from_json (value) } -> std::same_as<std::optional<T> >;
42
42
};
43
43
44
44
// / @ingroup json
@@ -131,24 +131,30 @@ auto to_json(const T &value) -> JSON {
131
131
// / If the value has a `.from_json()` static method, always prefer that
132
132
template <typename T>
133
133
requires (json_auto_has_method_from<T>)
134
- auto from_json (const JSON &value) -> T {
134
+ auto from_json (const JSON &value) -> std::optional<T> {
135
135
return T::from_json (value);
136
136
}
137
137
138
138
// / @ingroup json
139
139
template <typename T>
140
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 ();
141
+ auto from_json (const JSON &value) -> std::optional<T> {
142
+ if (value.is_boolean ()) {
143
+ return value.to_boolean ();
144
+ } else {
145
+ return std::nullopt;
146
+ }
144
147
}
145
148
146
149
// / @ingroup json
147
150
template <typename T>
148
151
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
+ auto from_json (const JSON &value) -> std::optional<T> {
153
+ if (value.is_integer ()) {
154
+ return static_cast <T>(value.to_integer ());
155
+ } else {
156
+ return std::nullopt;
157
+ }
152
158
}
153
159
154
160
// TODO: How can we keep this in the hash header that does not yet know about
@@ -177,20 +183,27 @@ auto to_json(const T &hash) -> JSON {
177
183
// / @ingroup json
178
184
template <typename T>
179
185
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 );
186
+ auto from_json (const JSON &value) -> std::optional<T> {
187
+ if (!value.is_array () || value.size () != 4 || !value.at (0 ).is_integer () ||
188
+ !value.at (1 ).is_integer () || !value.at (2 ).is_integer () ||
189
+ !value.at (3 ).is_integer ()) {
190
+ return std::nullopt;
191
+ }
192
+
183
193
#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 ))};
194
+ return T{(static_cast <__uint128_t >(
195
+ static_cast <std::uint64_t >(value.at (0 ).to_integer ()))
196
+ << 64 ) |
197
+ static_cast <std::uint64_t >(value.at (1 ).to_integer ()),
198
+ (static_cast <__uint128_t >(
199
+ static_cast <std::uint64_t >(value.at (2 ).to_integer ()))
200
+ << 64 ) |
201
+ static_cast <std::uint64_t >(value.at (3 ).to_integer ())};
189
202
#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 ))};
203
+ return T{ static_cast <std::uint64_t >(value.at (0 ). to_integer ( )),
204
+ static_cast <std::uint64_t >(value.at (1 ). to_integer ( )),
205
+ static_cast <std::uint64_t >(value.at (2 ). to_integer ( )),
206
+ static_cast <std::uint64_t >(value.at (3 ). to_integer ( ))};
194
207
#endif
195
208
}
196
209
@@ -204,16 +217,19 @@ auto to_json(const T &value) -> JSON {
204
217
// / @ingroup json
205
218
template <typename T>
206
219
requires std::is_same_v<T, JSON>
207
- auto from_json (const JSON &value) -> T {
220
+ auto from_json (const JSON &value) -> std::optional<T> {
208
221
return value;
209
222
}
210
223
211
224
// / @ingroup json
212
225
template <typename T>
213
226
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 ();
227
+ auto from_json (const JSON &value) -> std::optional<T> {
228
+ if (value.is_string ()) {
229
+ return value.to_string ();
230
+ } else {
231
+ return std::nullopt;
232
+ }
217
233
}
218
234
219
235
// / @ingroup json
@@ -226,10 +242,12 @@ auto to_json(const T value) -> JSON {
226
242
// / @ingroup json
227
243
template <typename T>
228
244
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 ());
245
+ auto from_json (const JSON &value) -> std::optional<T> {
246
+ if (value.is_integer ()) {
247
+ return static_cast <T>(value.to_integer ());
248
+ } else {
249
+ return std::nullopt;
250
+ }
233
251
}
234
252
235
253
// / @ingroup json
@@ -241,11 +259,17 @@ template <typename T> auto to_json(const std::optional<T> &value) -> JSON {
241
259
template <typename T>
242
260
requires requires { typename T::value_type; } &&
243
261
std::is_same_v<T, std::optional<typename T::value_type>>
244
- auto from_json (const JSON &value) -> T {
262
+ auto from_json (const JSON &value) -> std::optional<T> {
245
263
if (value.is_null ()) {
246
- return {};
264
+ return std::optional<T>{
265
+ std::optional<typename T::value_type>{std::nullopt}};
247
266
} else {
248
- return from_json<typename T::value_type>(value);
267
+ auto result{from_json<typename T::value_type>(value)};
268
+ if (!result.has_value ()) {
269
+ return std::nullopt;
270
+ }
271
+
272
+ return result;
249
273
}
250
274
}
251
275
@@ -302,45 +326,60 @@ auto to_json(
302
326
}
303
327
304
328
// / @ingroup json
305
- template <json_auto_list_like T> auto from_json (const JSON &value) -> T {
306
- assert (value.is_array ());
329
+ template <json_auto_list_like T>
330
+ auto from_json (const JSON &value) -> std::optional<T> {
331
+ if (!value.is_array ()) {
332
+ return std::nullopt;
333
+ }
334
+
307
335
T result;
308
336
309
337
if constexpr (requires { result.reserve (value.size ()); }) {
310
338
result.reserve (value.size ());
311
339
}
312
340
313
341
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));
342
+ auto subvalue{from_json<typename T::value_type>(item)};
343
+ if (!subvalue.has_value ()) {
344
+ return std::nullopt;
345
+ }
346
+
347
+ if constexpr (requires { result.insert (subvalue.value ()); }) {
348
+ result.insert (std::move (subvalue).value ());
318
349
} else {
319
- result.push_back (from_json< typename T::value_type>(item ));
350
+ result.push_back (std::move (subvalue). value ( ));
320
351
}
321
352
}
322
353
323
354
return result;
324
355
}
325
356
357
+ // / @ingroup json
326
358
template <json_auto_list_like T>
327
359
auto from_json (
328
360
const JSON &value,
329
- const std::function<typename T::value_type (const JSON &)> &callback) -> T {
330
- assert (value.is_array ());
361
+ const std::function<std::optional<typename T::value_type>(const JSON &)>
362
+ &callback) -> std::optional<T> {
363
+ if (!value.is_array ()) {
364
+ return std::nullopt;
365
+ }
366
+
331
367
T result;
332
368
333
369
if constexpr (requires { result.reserve (value.size ()); }) {
334
370
result.reserve (value.size ());
335
371
}
336
372
337
373
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));
374
+ auto subvalue{callback (item)};
375
+ if (!subvalue.has_value ()) {
376
+ return std::nullopt;
377
+ }
378
+
379
+ if constexpr (requires { result.insert (subvalue.value ()); }) {
380
+ result.insert (std::move (subvalue).value ());
342
381
} else {
343
- result.push_back (callback (item ));
382
+ result.push_back (std::move (subvalue). value ( ));
344
383
}
345
384
}
346
385
@@ -379,11 +418,20 @@ auto to_json(
379
418
}
380
419
381
420
// / @ingroup json
382
- template <json_auto_map_like T> auto from_json (const JSON &value) -> T {
383
- assert (value.is_object ());
421
+ template <json_auto_map_like T>
422
+ auto from_json (const JSON &value) -> std::optional<T> {
423
+ if (!value.is_object ()) {
424
+ return std::nullopt;
425
+ }
426
+
384
427
T result;
385
428
for (const auto &item : value.as_object ()) {
386
- result.emplace (item.first , from_json<typename T::mapped_type>(item.second ));
429
+ auto subvalue{from_json<typename T::mapped_type>(item.second )};
430
+ if (!subvalue.has_value ()) {
431
+ return std::nullopt;
432
+ }
433
+
434
+ result.emplace (item.first , std::move (subvalue).value ());
387
435
}
388
436
389
437
return result;
@@ -393,11 +441,20 @@ template <json_auto_map_like T> auto from_json(const JSON &value) -> T {
393
441
template <json_auto_map_like T>
394
442
auto from_json (
395
443
const JSON &value,
396
- const std::function<typename T::mapped_type (const JSON &)> &callback) -> T {
397
- assert (value.is_object ());
444
+ const std::function<std::optional<typename T::mapped_type>(const JSON &)>
445
+ &callback) -> std::optional<T> {
446
+ if (!value.is_object ()) {
447
+ return std::nullopt;
448
+ }
449
+
398
450
T result;
399
451
for (const auto &item : value.as_object ()) {
400
- result.emplace (item.first , callback (item.second ));
452
+ auto subvalue{callback (item.second )};
453
+ if (!subvalue.has_value ()) {
454
+ return std::nullopt;
455
+ }
456
+
457
+ result.emplace (item.first , std::move (subvalue).value ());
401
458
}
402
459
403
460
return result;
@@ -424,12 +481,19 @@ auto to_json(const std::pair<L, R> &value) -> JSON {
424
481
// / @ingroup json
425
482
template <typename T>
426
483
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 );
484
+ auto from_json (const JSON &value) -> std::optional<T> {
485
+ if (!value.is_array () || value.size () != 2 ) {
486
+ return std::nullopt;
487
+ }
488
+
489
+ auto first{from_json<typename T::first_type>(value.at (0 ))};
490
+ auto second{from_json<typename T::second_type>(value.at (1 ))};
491
+ if (!first.has_value () || !second.has_value ()) {
492
+ return std::nullopt;
493
+ }
494
+
430
495
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 )));
496
+ std::move (first).value (), std::move (second).value ());
433
497
}
434
498
435
499
// Handle 1-element tuples
@@ -442,10 +506,18 @@ template <json_auto_tuple_mono T> auto to_json(const T &value) -> JSON {
442
506
}
443
507
444
508
// / @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 ))};
509
+ template <json_auto_tuple_mono T>
510
+ auto from_json (const JSON &value) -> std::optional<T> {
511
+ if (!value.is_array () || value.size () != 1 ) {
512
+ return std::nullopt;
513
+ }
514
+
515
+ auto first{from_json<std::tuple_element_t <0 , T>>(value.at (0 ))};
516
+ if (!first.has_value ()) {
517
+ return std::nullopt;
518
+ }
519
+
520
+ return {std::move (first).value ()};
449
521
}
450
522
451
523
// / @ingroup json
@@ -463,16 +535,25 @@ template <json_auto_tuple_poly T> auto to_json(const T &value) -> JSON {
463
535
template <typename T, std::size_t ... Indices>
464
536
auto from_json_tuple_poly (const JSON &value, std::index_sequence<Indices...>)
465
537
-> T {
466
- return {from_json<std::tuple_element_t <Indices, T>>(value.at (Indices))...};
538
+ return {from_json<std::tuple_element_t <Indices, T>>(value.at (Indices))
539
+ .value ()...};
467
540
}
468
541
#endif
469
542
470
543
// / @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>>{});
544
+ template <json_auto_tuple_poly T>
545
+ auto from_json (const JSON &value) -> std::optional<T> {
546
+ if (!value.is_array () || value.size () != std::tuple_size_v<T>) {
547
+ return std::nullopt;
548
+ }
549
+
550
+ try {
551
+ return from_json_tuple_poly<T>(
552
+ value, std::make_index_sequence<std::tuple_size_v<T>>{});
553
+ // TODO: Maybe there is a better way to catch this without using exceptions?
554
+ } catch (const std::bad_optional_access &) {
555
+ return std::nullopt;
556
+ }
476
557
}
477
558
478
559
} // namespace sourcemeta::core
0 commit comments