simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
std_deserialize.h
1#if SIMDJSON_SUPPORTS_CONCEPTS
2
3#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
4#ifndef SIMDJSON_CONDITIONAL_INCLUDE
5#define SIMDJSON_ONDEMAND_DESERIALIZE_H
6#include "simdjson/generic/ondemand/object.h"
7#include "simdjson/generic/ondemand/array.h"
8#include "simdjson/generic/ondemand/base.h"
10#endif // SIMDJSON_CONDITIONAL_INCLUDE
11
12#include <concepts>
13#include <limits>
14#if SIMDJSON_STATIC_REFLECTION
15#include <meta>
16#include <vector>
17// #include <static_reflection> // for std::define_static_string - header not available yet
18#endif
19
20namespace simdjson {
21
23// Number deserialization
25
26template <std::unsigned_integral T>
27error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
28 using limits = std::numeric_limits<T>;
29
30 uint64_t x;
31 SIMDJSON_TRY(val.get_uint64().get(x));
32 if (x > (limits::max)()) {
34 }
35 out = static_cast<T>(x);
36 return SUCCESS;
37}
38
39template <std::floating_point T>
40error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
41 if constexpr (std::is_same_v<T, float>) {
42 // Going through binary64 and then rounding to binary32 would round twice
43 // and could produce a value that is not the float nearest to the JSON
44 // number, so we parse to binary32 directly.
45 return val.get_float().get(out);
46#if SIMDJSON_SUPPORTS_FLOAT32_T
47 } else if constexpr (std::is_same_v<T, std::float32_t>) {
48 // Same reason as float.
49 float x;
50 SIMDJSON_TRY(val.get_float().get(x));
51 out = static_cast<T>(x);
52 return SUCCESS;
53#endif // SIMDJSON_SUPPORTS_FLOAT32_T
54 } else {
55 double x;
56 SIMDJSON_TRY(val.get_double().get(x));
57 out = static_cast<T>(x);
58 return SUCCESS;
59 }
60}
61
62template <std::signed_integral T>
63error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
64 using limits = std::numeric_limits<T>;
65
66 int64_t x;
67 SIMDJSON_TRY(val.get_int64().get(x));
68 if (x > (limits::max)() || x < (limits::min)()) {
70 }
71 out = static_cast<T>(x);
72 return SUCCESS;
73}
74
76// String deserialization
78
79// just a character!
80error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
81 std::string_view x;
82 SIMDJSON_TRY(val.get_string().get(x));
83 if(x.size() != 1) {
84 return INCORRECT_TYPE;
85 }
86 out = x[0];
87 return SUCCESS;
88}
89
90// any string-like type (can be constructed from std::string_view)
91template <concepts::constructible_from_string_view T, typename ValT>
92error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
93 std::string_view str;
94 SIMDJSON_TRY(val.get_string().get(str));
95 if constexpr (requires { out.assign(str.data(), str.size()); }) {
96 // Copy straight into out (e.g., std::string): building a temporary and
97 // move-assigning it is markedly slower.
98 out.assign(str.data(), str.size());
99 } else {
100 out = T{str};
101 }
102 return SUCCESS;
103}
104
105#if SIMDJSON_SUPPORTS_CHAR8_T
106// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
107// such as std::u8string
108template <concepts::constructible_from_u8string_view T, typename ValT>
109error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
110 std::u8string_view str;
111 SIMDJSON_TRY(val.get_u8string().get(str));
112 if constexpr (requires { out.assign(str.data(), str.size()); }) {
113 // Copy straight into out (e.g., std::u8string), as for std::string above.
114 out.assign(str.data(), str.size());
115 } else {
116 out = T{str};
117 }
118 return SUCCESS;
119}
120#endif // SIMDJSON_SUPPORTS_CHAR8_T
121
122
123namespace details {
124// Whether to deserialize the elements of the container T directly into a new
125// element (emplace_one(out) and then get) rather than into a temporary that is
126// then moved into the container. A temporary of a trivially copyable type lives
127// in registers and is cheap to move, and value-initializing a small element in
128// place costs more than it saves (GCC zeroes it with rep stos). But moving a
129// large element, or a string (whose deserialization then needs a temporary
130// string and a move assignment of its own), is expensive.
131template <typename T>
132concept deserialize_in_place =
133 concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
134 !std::is_trivially_copyable_v<typename T::value_type> &&
135 (sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
136
137// Removes the last element of the container on destruction while armed.
138template <typename T>
139struct pop_back_guard {
140 T &container;
141 bool armed{true};
142 ~pop_back_guard() {
143 if (armed) { container.pop_back(); }
144 }
145};
146} // namespace details
147
155template <concepts::appendable_containers T, typename ValT>
156error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
157 using value_type = typename std::remove_cvref_t<T>::value_type;
158 static_assert(
159 deserializable<value_type, ValT>,
160 "The specified type inside the container must itself be deserializable");
161 static_assert(
162 std::is_default_constructible_v<value_type>,
163 "The specified type inside the container must default constructible.");
164 SIMDJSON_IMPLEMENTATION::ondemand::array arr;
165 if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, SIMDJSON_IMPLEMENTATION::ondemand::array>) {
166 arr = val;
167 } else {
168 SIMDJSON_TRY(val.get_array().get(arr));
169 }
170
171 if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
172 // Collect the elements in a per-thread scratch vector that keeps its
173 // capacity from call to call, then move them into out after reserving the
174 // exact size: out is allocated once instead of being regrown. A nested
175 // array of the same type finds the scratch busy and takes the paths below.
176 struct scratch_space {
177 std::vector<value_type> elements{};
178 bool busy{false};
179 };
180 static thread_local scratch_space scratch;
181 if (!scratch.busy && out.empty()) {
182 struct release_scratch {
183 scratch_space &s;
184 T &out;
185 size_t parsed{0};
186 bool complete{false};
187 // On an error or an exception, out gets the elements parsed so far (as
188 // with the loops below), without allocating. Kept out of the hot path.
189 simdjson_never_inline void keep_parsed() noexcept {
190 s.elements.resize(parsed);
191 out.swap(s.elements);
192 }
193 ~release_scratch() {
194 if (simdjson_unlikely(!complete)) { keep_parsed(); }
195 s.elements.clear();
196 // Do not hold on to the memory of a very large array.
197 if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
198 s.busy = false;
199 }
200 } release{scratch, out};
201 scratch.busy = true;
202 for (auto v : arr) {
203 SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
204 release.parsed++;
205 }
206 out.reserve(release.parsed);
207 release.complete = true;
208 for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
209 return SUCCESS;
210 }
211 }
212 if constexpr (details::deserialize_in_place<T>) {
213 for (auto v : arr) {
214 auto &slot = concepts::emplace_one(out);
215 // An error or an exception (a user tag_invoke may throw) must not leave
216 // a partially deserialized element behind.
217 details::pop_back_guard<T> guard{out};
218 SIMDJSON_TRY(v.get<value_type>(slot));
219 guard.armed = false;
220 }
221 } else {
222 for (auto v : arr) {
223 // Deserialize into a temporary first: an error or an exception (a user
224 // tag_invoke may throw) must not leave a default-constructed element behind.
225 value_type temp;
226 if (auto const err = v.get<value_type>(temp); err) {
227 return err;
228 }
229 concepts::emplace_one(out, std::move(temp));
230 }
231 }
232 return SUCCESS;
233}
234
235
240 template <concepts::string_view_keyed_map T, typename ValT>
241error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
242 using value_type = typename std::remove_cvref_t<T>::mapped_type;
243 static_assert(
244 deserializable<value_type, ValT>,
245 "The specified value type inside the container must itself be deserializable");
246 static_assert(
247 std::is_default_constructible_v<value_type>,
248 "The specified value type inside the container must default constructible.");
249 SIMDJSON_IMPLEMENTATION::ondemand::object obj;
250 SIMDJSON_TRY(val.get_object().get(obj));
251 for (auto field : obj) {
252 std::string_view key;
253 SIMDJSON_TRY(field.unescaped_key().get(key));
254 value_type this_value;
255 SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
256 [[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
257 // unclear what to do if the key already exists
258 // if (result.second == false) {
259 // // key already exists
260 // }
261 }
262 (void)out;
263 return SUCCESS;
264}
265
266template <concepts::string_view_keyed_map T>
267error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::object &obj, T &out) noexcept(false) {
268 using value_type = typename std::remove_cvref_t<T>::mapped_type;
269
270 out.clear();
271 for (auto field : obj) {
272 std::string_view key;
273 SIMDJSON_TRY(field.unescaped_key().get(key));
274
275 SIMDJSON_IMPLEMENTATION::ondemand::value value_obj;
276 SIMDJSON_TRY(field.value().get(value_obj));
277
278 value_type this_value;
279 SIMDJSON_TRY(value_obj.get(this_value));
280 out.emplace(typename T::key_type(key), std::move(this_value));
281 }
282 return SUCCESS;
283}
284
285template <concepts::string_view_keyed_map T>
286error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::value &val, T &out) noexcept(false) {
287 SIMDJSON_IMPLEMENTATION::ondemand::object obj;
288 SIMDJSON_TRY(val.get_object().get(obj));
289 return simdjson::deserialize(obj, out);
290}
291
292template <concepts::string_view_keyed_map T>
293error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::document &doc, T &out) noexcept(false) {
294 SIMDJSON_IMPLEMENTATION::ondemand::object obj;
295 SIMDJSON_TRY(doc.get_object().get(obj));
296 return simdjson::deserialize(obj, out);
297}
298
299template <concepts::string_view_keyed_map T>
300error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::document_reference &doc, T &out) noexcept(false) {
301 SIMDJSON_IMPLEMENTATION::ondemand::object obj;
302 SIMDJSON_TRY(doc.get_object().get(obj));
303 return simdjson::deserialize(obj, out);
304}
305
306
317template <concepts::smart_pointer T, typename ValT>
318error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
319 using element_type = typename std::remove_cvref_t<T>::element_type;
320
321 // For better error messages, don't use these as constraints on
322 // the tag_invoke CPO.
323 static_assert(
324 deserializable<element_type, ValT>,
325 "The specified type inside the unique_ptr must itself be deserializable");
326 static_assert(
327 std::is_default_constructible_v<element_type>,
328 "The specified type inside the unique_ptr must default constructible.");
329
330 // Own the allocation before get(): a user tag_invoke may throw.
331 std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
332 if (!ptr) {
333 return MEMALLOC;
334 }
335 SIMDJSON_TRY(val.template get<element_type>(*ptr));
336 out = std::move(ptr);
337 return SUCCESS;
338}
339
343template <concepts::optional_type T>
344error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
345 using value_type = typename std::remove_cvref_t<T>::value_type;
346
347 // Check if the value is null
348 bool is_null_value;
349 SIMDJSON_TRY( val.is_null().get(is_null_value) );
350 if (is_null_value) {
351 out.reset(); // Set to nullopt
352 return SUCCESS;
353 }
354
355 if (!out) {
356 out.emplace();
357 }
358 SIMDJSON_TRY(val.template get<value_type>(out.value()));
359 return SUCCESS;
360}
361
362
363#if SIMDJSON_STATIC_REFLECTION
364
365
366template <typename T>
367constexpr bool user_defined_type = (std::is_class_v<T>
368&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
369#if SIMDJSON_SUPPORTS_CHAR8_T
370// The char8_t string types are class types with no reflectable members, so
371// without this they would be taken for user structs and the reflection
372// overload below would compete with the u8 string overload in
373// std_deserialize.h, making every tag_invoke call on them ambiguous.
374&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
375#endif // SIMDJSON_SUPPORTS_CHAR8_T
376&& !concepts::optional_type<T> &&
377!concepts::appendable_containers<T>
378// simdjson's own types (array, object, value, raw_json_string, number,
379// document, document_reference) have dedicated get<T>() specializations and
380// must never go through reflection.
381&& !is_builtin_deserializable_v<T>
382&& !std::is_same_v<T, SIMDJSON_IMPLEMENTATION::ondemand::number>
383&& !std::is_same_v<T, SIMDJSON_IMPLEMENTATION::ondemand::document>
384&& !std::is_same_v<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>);
385
386
387// key_selector_reflection_detail is defined unconditionally (it only requires
388// static reflection). It provides the compile-time machinery for building a
389// key_selector from a struct's members, the per-member helpers shared by every
390// deserialization path (they implement the annotations of annotations.h), the
391// ordered per-member path used by the opt-out build (see
392// deserialize_struct_ordered below), and the scan used for structs annotated
393// with deny_unknown_fields and as an automatic fallback (see
394// deserialize_struct_scan and keys_fit_selector below).
395namespace key_selector_reflection_detail {
396
397// A member participates if it is public, non-const, and not annotated with skip
398// or skip_deserializing.
399consteval bool is_eligible_member(std::meta::info mem) {
400 return !std::meta::is_const(mem) && std::meta::is_public(mem)
401 && !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
402 && !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
403}
404
405// A field is a data member reached from T through a chain of members: usually a
406// direct member of T (a path of length one), or a member of a member annotated
407// with flatten (the members of a flattened structure are fields of the enclosing
408// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
409template <std::meta::info First, std::meta::info... Rest>
410struct member_path {
411 // The data member holding the value; its annotations drive (de)serialization.
412 static constexpr std::meta::info leaf = [] {
413 std::meta::info members[] = {First, Rest...};
414 return members[sizeof...(Rest)];
415 }();
416 template <typename T>
417 static simdjson_inline constexpr auto &get(T &obj) noexcept {
418 if constexpr (sizeof...(Rest) == 0) {
419 return obj.[:First:];
420 } else {
421 return member_path<Rest...>::get(obj.[:First:]);
422 }
423 }
424};
425
426// True when T can be flattened: a structure deserialized member by member (not
427// a string, a container, an optional, a smart pointer, ...).
428template <typename T>
429constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
430 && !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
431
432consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
433 std::vector<std::meta::info> &fields) {
434 for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
435 if (!is_eligible_member(mem)) { continue; }
436 prefix.push_back(std::meta::reflect_constant(mem));
437 if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
438 std::meta::info flattened = simdjson::detail::flattened_type(mem);
439 if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
440 throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
441 }
442 append_eligible_fields(flattened, prefix, fields);
443 } else {
444 fields.push_back(std::meta::substitute(^^member_path, prefix));
445 }
446 prefix.pop_back();
447 }
448}
449
450// The fields of `type` that participate in deserialization, in declaration order
451// (the fields of a flattened member take its place). A field's position in this
452// list is its "field index" below.
453consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
454 std::vector<std::meta::info> prefix;
455 std::vector<std::meta::info> fields;
456 append_eligible_fields(type, prefix, fields);
457 return fields;
458}
459
460// The data member at the end of a field path.
461consteval std::meta::info field_leaf(std::meta::info path) {
462 return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
463}
464
465// Number of fields that participate in deserialization. A class can have zero
466// eligible fields (e.g. std::chrono::time_point, whose only data member is
467// private): an empty key_selector cannot be built, so the tag_invoke below
468// special-cases this count.
469template <typename T>
470consteval std::size_t eligible_field_count() {
471 return eligible_fields(^^T).size();
472}
473
474// The keys accepted for a member (or an enumerator): its JSON key followed by its
475// aliases. They are static strings, so they can be used as template arguments.
476consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
477 std::vector<const char *> keys;
478 for (std::string_view key : simdjson::detail::json_key_names(entity)) {
479 bool repeated = false;
480 for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
481 if (!repeated) { keys.push_back(std::define_static_string(key)); }
482 }
483 return keys;
484}
485
486// Every key accepted for T: the eligible fields in order, each followed by its
487// aliases. This is the key list of T's key_selector.
488consteval std::vector<const char *> accepted_keys(std::meta::info type) {
489 std::vector<const char *> keys;
490 for (std::meta::info path : eligible_fields(type)) {
491 for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
492 }
493 return keys;
494}
495
496// The field index of each entry of accepted_keys(type).
497consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
498 std::vector<std::size_t> key_fields;
499 std::vector<std::meta::info> fields = eligible_fields(type);
500 for (std::size_t i = 0; i < fields.size(); ++i) {
501 for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
502 }
503 return key_fields;
504}
505
506// True when no two eligible fields of T accept the same key. Otherwise one JSON
507// key would have to fill several members: this is reported at compile time.
508template <typename T>
509consteval bool accepted_keys_are_distinct() {
510 std::vector<const char *> keys = accepted_keys(^^T);
511 for (std::size_t i = 0; i < keys.size(); ++i) {
512 for (std::size_t j = i + 1; j < keys.size(); ++j) {
513 if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
514 }
515 }
516 return true;
517}
518
519// True when some accepted key of T is written with escape sequences in JSON (a
520// double quote, a backslash or a control character). Such a key can only be
521// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
522// the key_selector compare the raw bytes.
523template <typename T>
524consteval bool keys_need_unescaping() {
525 for (std::string_view key : accepted_keys(^^T)) {
526 for (char c : key) {
527 if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
528 }
529 }
530 return false;
531}
532
533// True when some eligible field of T has aliases: several selector keys may then
534// map to the same field.
535template <typename T>
536consteval bool has_aliases() {
537 return accepted_keys(^^T).size() != eligible_fields(^^T).size();
538}
539
540// True when `mem` is annotated with default_value or default_from (directly, or
541// through a default_value annotation on the enclosing structure).
542template <auto mem>
543consteval bool has_default() {
544 return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
545 || simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
546 || simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
547}
548
549// True when a missing key for `mem` is not an error: optional members and
550// members with a default.
551template <auto mem>
552consteval bool may_be_absent() {
553 return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
554}
555
556// True when every eligible field of T is required (none may be absent). In that
557// case presence can be checked with a single match count instead of a per-field
558// "seen" array.
559template <typename T>
560consteval bool all_eligible_fields_required() {
561 bool all_required = true;
562 template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
563 if constexpr (may_be_absent<[: path :]::leaf>()) {
564 all_required = false;
565 }
566 }
567 return all_required;
568}
569
570// `key` as a constevalutil::fixed_string usable as an NTTP.
571template <const char *key>
572consteval auto key_fixed_string() {
573 constexpr std::string_view key_view{ key };
574 char buffer[key_view.size() + 1] = {};
575 for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
576 return constevalutil::fixed_string<key_view.size() + 1>(buffer);
577}
578
579// key_selector template arguments (one fixed_string per accepted key), in the
580// order of accepted_keys.
581template <typename T>
582consteval std::vector<std::meta::info> selector_key_args() {
583 std::vector<std::meta::info> args;
584 template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
585 args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
586 }
587 return args;
588}
589
590// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
591template <typename T>
592using selector_for = typename [: std::meta::substitute(
593 ^^SIMDJSON_IMPLEMENTATION::ondemand::key_selector, selector_key_args<T>()) :];
594
595// True when T's accepted keys satisfy every key_selector requirement, so a
596// key_selector can be built for T without a compile-time error. This mirrors the
597// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
598// and at most 63 characters, no backslash / double-quote / null byte, and all
599// keys distinct. Keys with any other control character are excluded too: they
600// are escaped in JSON, so their raw bytes never match. When this returns false
601// the deserializer falls back to deserialize_struct_scan instead of failing to
602// compile.
603template <typename T>
604consteval bool keys_fit_selector() {
605 std::vector<std::string_view> keys;
606 for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
607 if (keys.size() > 255) { return false; }
608 for (std::size_t i = 0; i < keys.size(); ++i) {
609 if (keys[i].empty() || keys[i].size() > 63) { return false; }
610 for (char c : keys[i]) {
611 if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
612 }
613 for (std::size_t j = i + 1; j < keys.size(); ++j) {
614 if (keys[i] == keys[j]) { return false; }
615 }
616 }
617 return true;
618}
619
620// True when the class `adapter` declares a member named deserialize.
621consteval bool declares_deserialize(std::meta::info adapter) {
622 for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
623 if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
624 }
625 return false;
626}
627
628// Deserialize a JSON value into `target`, the storage of member `mem`, through
629// the member's with<Adapter> annotation when the adapter provides a deserialize
630// function.
631template <auto mem, typename ValueT, typename M>
632simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
633 constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
634 if constexpr (with_type != std::meta::info{}) {
635 using adapter = typename [: with_type :]::adapter;
636 using ondemand_value = SIMDJSON_IMPLEMENTATION::ondemand::value;
637 if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
638 return adapter::deserialize(field_value, target);
639 } else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
640 && requires { field_value.get_value(); }) {
641 // A transparent structure read from a document: the adapter takes an
642 // ondemand::value. A scalar document cannot be viewed as a value, so it
643 // reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
644 // document itself and has no such limitation).
645 ondemand_value v;
646 SIMDJSON_TRY(field_value.get_value().get(v));
647 return adapter::deserialize(v, target);
648 } else {
649 static_assert(!declares_deserialize(^^adapter),
650 "the deserialize function of a simdjson::with adapter must be callable as "
651 "Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
652 return field_value.get(target);
653 }
654 } else {
655 return field_value.get(target);
656 }
657}
658
659// Deserialize the storage `target` of member `mem` from a JSON value.
660template <auto mem, typename ValueT, typename M>
661simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
662 if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
663 // A present key replaces the default value: deserialize into a fresh
664 // temporary so that, e.g., a container does not append to its default
665 // content, and a failure leaves the default untouched.
666 M value{};
667 SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
668 target = std::move(value);
669 return SUCCESS;
670 } else {
671 return deserialize_member_value<mem>(field_value, target);
672 }
673}
674
675// Deserialize the field with the given field index.
676template <typename T>
677simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
678 std::size_t field_index, SIMDJSON_IMPLEMENTATION::ondemand::value field_value, T &out) noexcept(false) {
679 std::size_t counter = 0;
680 template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
681 using field = [: path :];
682 if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
683 ++counter;
684 }
685 return SUCCESS;
686}
687
688// Called when the key(s) of member `mem`, stored in `target`, are missing from
689// the JSON object: an error for a required member, a call to the default_from
690// factory, or nothing at all.
691template <auto mem, typename M>
692simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
693 constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
694 if constexpr (default_from_type != std::meta::info{}) {
695 target = [: default_from_type :]::factory();
696 return SUCCESS;
697 } else if constexpr (may_be_absent<mem>()) {
698 // For optional and default_value members, a missing key is not an error:
699 // leave the member at its current (default) value.
700 (void)target;
701 return SUCCESS;
702 } else {
703 (void)target;
704 return NO_SUCH_FIELD;
705 }
706}
707
708// Report or handle every field that was not seen in the JSON object.
709template <typename T, std::size_t N>
710simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
711 const std::array<bool, N> &seen_field, T &out) noexcept(false) {
712 std::size_t counter = 0;
713 template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
714 using field = [: path :];
715 if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
716 ++counter;
717 }
718 return SUCCESS;
719}
720
721// Ordered, per-field deserialization: one obj[key] lookup per eligible field
722// (and per alias, until one is found). This is the opt-out path
723// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
724// that need unescaping (see keys_need_unescaping).
725template <typename T>
726simdjson_warn_unused error_code deserialize_struct_ordered(
727 SIMDJSON_IMPLEMENTATION::ondemand::object &obj, T &out) noexcept(false) {
728 template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
729 using field = [: path :];
730 SIMDJSON_IMPLEMENTATION::ondemand::value field_value;
732 template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
733 if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
734 }
735 if (error == NO_SUCH_FIELD) {
736 SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
737 } else if (error) {
738 return error;
739 } else {
740 SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
741 }
742 }
743 return SUCCESS;
744}
745
746// Appends the JSON keys that serialization writes for members of `type` that
747// deserialization cannot assign (const or non-public members, directly or
748// through flatten). `all` is true inside a flattened member that is itself
749// unassignable. Keys of skip_deserializing members are not included: they are
750// unknown keys, as documented.
751consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
752 for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
753 if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
754 || simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
755 || simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
756 continue;
757 }
758 bool unassignable = all || !is_eligible_member(mem);
759 if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
760 append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
761 } else if (unassignable) {
762 keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
763 }
764 }
765}
766
767consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
768 std::vector<const char *> keys;
769 append_unassignable_keys(type, false, keys);
770 return keys;
771}
772
773// Deserialization by a single pass over every field of the object, comparing
774// unescaped keys. It is used for structs annotated with deny_unknown_fields
775// (DenyUnknown = true), where a key that does not map to an eligible field is
776// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
777// key_selector or obj[key] cannot match (see keys_fit_selector and
778// keys_need_unescaping). As with object::for_each, the first occurrence of a
779// field wins (later duplicates, or aliases of a field already seen, are ignored).
780template <bool DenyUnknown, typename T>
781simdjson_warn_unused error_code deserialize_struct_scan(
782 SIMDJSON_IMPLEMENTATION::ondemand::object &obj, T &out) noexcept(false) {
783 static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
784 static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
785 std::array<bool, eligible_field_count<T>()> seen_field{};
786 for (auto field_result : obj) {
787 SIMDJSON_IMPLEMENTATION::ondemand::field json_field;
788 SIMDJSON_TRY(std::move(field_result).get(json_field));
789 std::string_view key;
790 SIMDJSON_TRY(json_field.unescaped_key().get(key));
791 std::size_t key_index = keys.size();
792 for (std::size_t i = 0; i < keys.size(); ++i) {
793 if (key == std::string_view(keys[i])) { key_index = i; break; }
794 }
795 if (key_index == keys.size()) {
796 if constexpr (DenyUnknown) {
797 // A key that T itself serializes (e.g. of a const member) is not
798 // unknown: a serialized value must parse back.
799 static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
800 bool ignored = false;
801 for (const char *ignored_key : ignored_keys) {
802 if (key == std::string_view(ignored_key)) { ignored = true; break; }
803 }
804 if (!ignored) { return UNKNOWN_FIELD; }
805 }
806 continue;
807 }
808 const std::size_t field_index = key_fields[key_index];
809 if (seen_field[field_index]) { continue; }
810 seen_field[field_index] = true;
811 SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
812 }
813 return handle_missing_fields(seen_field, out);
814}
815
816template <typename T>
817consteval bool is_transparent() {
818 return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
819}
820
821} // namespace key_selector_reflection_detail
822
823// Deserialize a reflected struct. By default this builds a compile-time
824// key_selector from the struct's members and walks each object once with
825// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
826// per member. Other paths are used instead:
827// - globally, the ordered per-member path when defining
828// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
829// - automatically and per-type, a scan of the object comparing unescaped keys
830// when the struct's keys do not fit the key_selector limits (see
831// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
832// so that long member names and the like keep compiling rather than
833// tripping a static_assert.
834// Structs annotated with deny_unknown_fields always use the strict scan, and
835// structs annotated with transparent are deserialized as their single member.
836//
837// noexcept(false): a member's tag_invoke may throw and the exception must reach
838// the caller of get<T>().
839template <typename T, typename ValT>
840 requires(user_defined_type<T> && std::is_class_v<T>)
841error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
842 if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
843 constexpr auto mem = simdjson::detail::transparent_member(^^T);
844 if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, SIMDJSON_IMPLEMENTATION::ondemand::object>) {
845 // We were handed an object: only a structure can be deserialized from it.
846 if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
847 return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
848 } else {
849 return INCORRECT_TYPE;
850 }
851 } else {
852 return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
853 }
854 } else {
855 static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
856 "two members of this structure accept the same JSON key (check rename, alias, "
857 "rename_all and flatten)");
858 SIMDJSON_IMPLEMENTATION::ondemand::object obj;
859 if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, SIMDJSON_IMPLEMENTATION::ondemand::object>) {
860 obj = val;
861 } else {
862 SIMDJSON_TRY(val.get_object().get(obj));
863 }
864 if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
865 return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
866 } else {
867#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
868 // Opt-out build: use the ordered per-member path, unless obj[key] cannot
869 // match T's keys.
870 if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
871 return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
872 } else {
873 return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
874 }
875#else
876 if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
877 // No fields to deserialize: an empty key_selector cannot be built, so just
878 // validate that the input is an object (done above) and succeed. Mirrors the
879 // ordered per-member path, which iterates over zero members.
880 (void)out;
881 (void)obj;
882 return SUCCESS;
883 } else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
884 // Automatic fallback: T's accepted keys do not fit the key_selector limits
885 // (e.g. a member name longer than 63 characters, or a key with a double
886 // quote), so building a selector would be a compile error. Scan the object
887 // instead, so the default never breaks a struct that the opt-out path would
888 // accept.
889 return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
890 } else {
891 using selector = key_selector_reflection_detail::selector_for<T>;
892 if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
893 && !key_selector_reflection_detail::has_aliases<T>()) {
894 // Fast path: every member is required and has a single key. A single
895 // for_each pass parses each matched field; the returned match count then
896 // tells us whether every member was present (matched_count ==
897 // selector::size()) without a per-member "seen" array. A value-parse error
898 // (e.g. a type mismatch) is propagated by for_each.
899 auto walk = obj.template for_each<selector>(
900 [&](std::size_t matched_index, SIMDJSON_IMPLEMENTATION::ondemand::value field_value) -> error_code {
901 std::size_t counter = 0;
902 template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
903 using field = [: path :];
904 if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
905 ++counter;
906 }
907 return SUCCESS;
908 });
909 if (walk.error) { return walk.error; }
910 // A missing required member shows up as a short match count and is reported as
911 // NO_SUCH_FIELD, mirroring the ordered obj[key] path.
912 if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
913 return SUCCESS;
914 } else {
915 static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
916 std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
917 // Single pass over the object: each field whose key matches a member (or one
918 // of its aliases) yields its selector index, which we map back to the
919 // corresponding member. The first key seen for a member wins. The callback
920 // returns an error_code so that a value-parse error (e.g. a type mismatch on
921 // a matched field) is propagated by for_each instead of being silently dropped.
922 error_code walk_error = obj.template for_each<selector>(
923 [&](std::size_t matched_index, SIMDJSON_IMPLEMENTATION::ondemand::value field_value) -> error_code {
924 const std::size_t field_index = key_fields[matched_index];
925 if (seen_field[field_index]) { return SUCCESS; }
926 seen_field[field_index] = true;
927 return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
928 });
929 if (walk_error) { return walk_error; }
930 // Required members must be present: a missing one is reported as
931 // NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
932 // members may be absent.
933 return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
934 }
935 }
936#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
937 }
938 }
939}
940
941// Support for enum deserialization - deserialize from string representation using
942// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
943// (see rename and rename_all) and its aliases.
944template <typename T, typename ValT>
945 requires(std::is_enum_v<T>)
946error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
947#if SIMDJSON_STATIC_REFLECTION
948 std::string_view str;
949 SIMDJSON_TRY(val.get_string().get(str));
950 static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
951 template for (constexpr auto enum_val : enumerators) {
952 template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
953 if (str == std::string_view(key)) {
954 out = [:enum_val:];
955 return SUCCESS;
956 }
957 }
958 };
959
960 return INCORRECT_TYPE;
961#else
962 // Fallback: deserialize as integer if reflection not available
963 std::underlying_type_t<T> int_val;
964 SIMDJSON_TRY(val.get(int_val));
965 out = static_cast<T>(int_val);
966 return SUCCESS;
967#endif
968}
969
970template <typename simdjson_value, typename T>
971 requires(user_defined_type<std::remove_cvref_t<T>>)
972error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
973 std::unique_ptr<T> ptr(new (std::nothrow) T());
974 if (!ptr) {
975 return MEMALLOC;
976 }
977 SIMDJSON_TRY(val.get(*ptr));
978 out = std::move(ptr);
979 return SUCCESS;
980}
981
982template <typename simdjson_value, typename T>
983 requires(user_defined_type<std::remove_cvref_t<T>>)
984error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
985 std::shared_ptr<T> ptr(new (std::nothrow) T());
986 if (!ptr) {
987 return MEMALLOC;
988 }
989 SIMDJSON_TRY(val.get(*ptr));
990 out = std::move(ptr);
991 return SUCCESS;
992}
993
994#endif // SIMDJSON_STATIC_REFLECTION
995
997// Unique pointers
999error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
1000 bool is_null_value;
1001 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1002 if (is_null_value) {
1003 out.reset();
1004 return SUCCESS;
1005 }
1006 if (!out) {
1007 out = std::make_unique<bool>();
1008 if (!out) { return MEMALLOC; }
1009 }
1010 SIMDJSON_TRY(val.get_bool().get(*out));
1011 return SUCCESS;
1012}
1013
1014error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
1015 bool is_null_value;
1016 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1017 if (is_null_value) {
1018 out.reset();
1019 return SUCCESS;
1020 }
1021 if (!out) {
1022 out = std::make_unique<int64_t>();
1023 if (!out) { return MEMALLOC; }
1024 }
1025 SIMDJSON_TRY(val.get_int64().get(*out));
1026 return SUCCESS;
1027}
1028
1029error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
1030 bool is_null_value;
1031 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1032 if (is_null_value) {
1033 out.reset();
1034 return SUCCESS;
1035 }
1036 if (!out) {
1037 out = std::make_unique<uint64_t>();
1038 if (!out) { return MEMALLOC; }
1039 }
1040 SIMDJSON_TRY(val.get_uint64().get(*out));
1041 return SUCCESS;
1042}
1043
1044error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
1045 bool is_null_value;
1046 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1047 if (is_null_value) {
1048 out.reset();
1049 return SUCCESS;
1050 }
1051 if (!out) {
1052 out = std::make_unique<double>();
1053 if (!out) { return MEMALLOC; }
1054 }
1055 SIMDJSON_TRY(val.get_double().get(*out));
1056 return SUCCESS;
1057}
1058
1059error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
1060 bool is_null_value;
1061 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1062 if (is_null_value) {
1063 out.reset();
1064 return SUCCESS;
1065 }
1066 if (!out) {
1067 out = std::make_unique<std::string_view>();
1068 if (!out) { return MEMALLOC; }
1069 }
1070 SIMDJSON_TRY(val.get_string().get(*out));
1071 return SUCCESS;
1072}
1073
1074
1076// Shared pointers
1078error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
1079 bool is_null_value;
1080 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1081 if (is_null_value) {
1082 out.reset();
1083 return SUCCESS;
1084 }
1085 if (!out) {
1086 out = std::make_shared<bool>();
1087 if (!out) { return MEMALLOC; }
1088 }
1089 SIMDJSON_TRY(val.get_bool().get(*out));
1090 return SUCCESS;
1091}
1092
1093error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
1094 bool is_null_value;
1095 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1096 if (is_null_value) {
1097 out.reset();
1098 return SUCCESS;
1099 }
1100 if (!out) {
1101 out = std::make_shared<int64_t>();
1102 if (!out) { return MEMALLOC; }
1103 }
1104 SIMDJSON_TRY(val.get_int64().get(*out));
1105 return SUCCESS;
1106}
1107
1108error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
1109 bool is_null_value;
1110 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1111 if (is_null_value) {
1112 out.reset();
1113 return SUCCESS;
1114 }
1115 if (!out) {
1116 out = std::make_shared<uint64_t>();
1117 if (!out) { return MEMALLOC; }
1118 }
1119 SIMDJSON_TRY(val.get_uint64().get(*out));
1120 return SUCCESS;
1121}
1122
1123error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
1124 bool is_null_value;
1125 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1126 if (is_null_value) {
1127 out.reset();
1128 return SUCCESS;
1129 }
1130 if (!out) {
1131 out = std::make_shared<double>();
1132 if (!out) { return MEMALLOC; }
1133 }
1134 SIMDJSON_TRY(val.get_double().get(*out));
1135 return SUCCESS;
1136}
1137
1138error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
1139 bool is_null_value;
1140 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1141 if (is_null_value) {
1142 out.reset();
1143 return SUCCESS;
1144 }
1145 if (!out) {
1146 out = std::make_shared<std::string_view>();
1147 if (!out) { return MEMALLOC; }
1148 }
1149 SIMDJSON_TRY(val.get_string().get(*out));
1150 return SUCCESS;
1151}
1152
1153
1155// Explicit optional specializations
1157
1159// Explicit smart pointer specializations for string and int types
1161error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
1162 // Check if the value is null
1163 bool is_null_value;
1164 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1165 if (is_null_value) {
1166 out.reset(); // Set to nullptr
1167 return SUCCESS;
1168 }
1169
1170 if (!out) {
1171 out = std::make_unique<std::string>();
1172 }
1173 std::string_view str;
1174 SIMDJSON_TRY(val.get_string().get(str));
1175 *out = std::string{str};
1176 return SUCCESS;
1177}
1178
1179error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
1180 // Check if the value is null
1181 bool is_null_value;
1182 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1183 if (is_null_value) {
1184 out.reset(); // Set to nullptr
1185 return SUCCESS;
1186 }
1187
1188 if (!out) {
1189 out = std::make_shared<std::string>();
1190 }
1191 std::string_view str;
1192 SIMDJSON_TRY(val.get_string().get(str));
1193 *out = std::string{str};
1194 return SUCCESS;
1195}
1196
1197error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
1198 // Check if the value is null
1199 bool is_null_value;
1200 SIMDJSON_TRY( val.is_null().get(is_null_value) );
1201 if (is_null_value) {
1202 out.reset(); // Set to nullptr
1203 return SUCCESS;
1204 }
1205
1206 if (!out) {
1207 out = std::make_unique<int>();
1208 }
1209 int64_t temp;
1210 SIMDJSON_TRY(val.get_int64().get(temp));
1211 *out = static_cast<int>(temp);
1212 return SUCCESS;
1213}
1214
1215} // namespace simdjson
1216
1217#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
1218#endif // SIMDJSON_SUPPORTS_CONCEPTS
Provides compile-time annotations for simdjson structures.
element_type
The actual concrete type of a JSON element This is the type it is most easily cast to with get<>.
Definition element.h:16
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
error_code
All possible errors returned by simdjson.
Definition error.h:19
@ INCORRECT_TYPE
JSON element has a different type than user expected.
Definition error.h:37
@ NO_SUCH_FIELD
JSON field not found in object.
Definition error.h:40
@ UNKNOWN_FIELD
JSON field does not map to any member of the target (see simdjson::deny_unknown_fields)
Definition error.h:53
@ NUMBER_OUT_OF_RANGE
JSON number does not fit in 64 bits.
Definition error.h:38
@ MEMALLOC
Error allocating memory, most likely out of memory.
Definition error.h:22
@ SUCCESS
No error.
Definition error.h:20