#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H #ifndef SIMDJSON_CONDITIONAL_INCLUDE #define SIMDJSON_ONDEMAND_DESERIALIZE_H #include "simdjson/generic/ondemand/object.h" #include "simdjson/generic/ondemand/array.h" #include "simdjson/generic/ondemand/base.h" #include "simdjson/annotations.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE #include #include #if SIMDJSON_STATIC_REFLECTION #include #include // #include // for std::define_static_string - header not available yet #endif namespace simdjson { ////////////////////////////// // Number deserialization ////////////////////////////// template error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; uint64_t x; SIMDJSON_TRY(val.get_uint64().get(x)); if (x > (limits::max)()) { return NUMBER_OUT_OF_RANGE; } out = static_cast(x); return SUCCESS; } template error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); out = static_cast(x); return SUCCESS; } template error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; int64_t x; SIMDJSON_TRY(val.get_int64().get(x)); if (x > (limits::max)() || x < (limits::min)()) { return NUMBER_OUT_OF_RANGE; } out = static_cast(x); return SUCCESS; } ////////////////////////////// // String deserialization ////////////////////////////// // just a character! error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { std::string_view x; SIMDJSON_TRY(val.get_string().get(x)); if(x.size() != 1) { return INCORRECT_TYPE; } out = x[0]; return SUCCESS; } // any string-like type (can be constructed from std::string_view) template error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); out = T{str}; return SUCCESS; } /** * STL containers have several constructors including one that takes a single * size argument. Thus, some compilers (Visual Studio) will not be able to * disambiguate between the size and container constructor. Users should * explicitly specify the type of the container as needed: e.g., * doc.get>(). */ template error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( deserializable, "The specified type inside the container must itself be deserializable"); static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); SIMDJSON_IMPLEMENTATION::ondemand::array arr; if constexpr (std::is_same_v, SIMDJSON_IMPLEMENTATION::ondemand::array>) { arr = val; } else { SIMDJSON_TRY(val.get_array().get(arr)); } for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); err) { // If an error occurs, the empty element that we just inserted gets // removed. We're not using a temp variable because if T is a heavy // type, we want the valid path to be the fast path and the slow path be // the path that has errors in it. if constexpr (requires { out.pop_back(); }) { static_cast(out.pop_back()); } return err; } } else { value_type temp; if (auto const err = v.get().get(temp); err) { return err; } concepts::emplace_one(out, std::move(temp)); } } return SUCCESS; } /** * We want to support std::map and std::unordered_map but only for * string-keyed types. */ template error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( deserializable, "The specified value type inside the container must itself be deserializable"); static_assert( std::is_default_constructible_v, "The specified value type inside the container must default constructible."); SIMDJSON_IMPLEMENTATION::ondemand::object obj; SIMDJSON_TRY(val.get_object().get(obj)); for (auto field : obj) { std::string_view key; SIMDJSON_TRY(field.unescaped_key().get(key)); value_type this_value; SIMDJSON_TRY(field.value().get().get(this_value)); [[maybe_unused]] std::pair result = out.emplace(key, this_value); // unclear what to do if the key already exists // if (result.second == false) { // // key already exists // } } (void)out; return SUCCESS; } template error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::object &obj, T &out) noexcept { using value_type = typename std::remove_cvref_t::mapped_type; out.clear(); for (auto field : obj) { std::string_view key; SIMDJSON_TRY(field.unescaped_key().get(key)); SIMDJSON_IMPLEMENTATION::ondemand::value value_obj; SIMDJSON_TRY(field.value().get(value_obj)); value_type this_value; SIMDJSON_TRY(value_obj.get(this_value)); out.emplace(typename T::key_type(key), std::move(this_value)); } return SUCCESS; } template error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::value &val, T &out) noexcept { SIMDJSON_IMPLEMENTATION::ondemand::object obj; SIMDJSON_TRY(val.get_object().get(obj)); return simdjson::deserialize(obj, out); } template error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::document &doc, T &out) noexcept { SIMDJSON_IMPLEMENTATION::ondemand::object obj; SIMDJSON_TRY(doc.get_object().get(obj)); return simdjson::deserialize(obj, out); } template error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::document_reference &doc, T &out) noexcept { SIMDJSON_IMPLEMENTATION::ondemand::object obj; SIMDJSON_TRY(doc.get_object().get(obj)); return simdjson::deserialize(obj, out); } /** * This CPO (Customization Point Object) will help deserialize into * smart pointers. * * If constructing T is nothrow, this conversion should be nothrow as well since * we return MEMALLOC if we're not able to allocate memory instead of throwing * the error message. * * @tparam T The type inside the smart pointer * @tparam ValT document/value type * @param val document/value * @param out a reference to the smart pointer * @return status of the conversion */ template error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; // For better error messages, don't use these as constraints on // the tag_invoke CPO. static_assert( deserializable, "The specified type inside the unique_ptr must itself be deserializable"); static_assert( std::is_default_constructible_v, "The specified type inside the unique_ptr must default constructible."); auto ptr = new (std::nothrow) element_type(); if (ptr == nullptr) { return MEMALLOC; } SIMDJSON_TRY(val.template get(*ptr)); out.reset(ptr); return SUCCESS; } /** * This CPO (Customization Point Object) will help deserialize into optional types. */ template error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; // Check if the value is null bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); // Set to nullopt return SUCCESS; } if (!out) { out.emplace(); } SIMDJSON_TRY(val.template get(out.value())); return SUCCESS; } #if SIMDJSON_STATIC_REFLECTION template constexpr bool user_defined_type = (std::is_class_v && !std::is_same_v && !std::is_same_v && !concepts::optional_type && !concepts::appendable_containers); #if defined(SIMDJSON_USE_KEY_SELECTOR_REFLECTION) && SIMDJSON_USE_KEY_SELECTOR_REFLECTION // Experimental: deserialize a reflected struct using a compile-time key_selector // and a single object::for_each pass (perfect-hash key matching) instead of one // obj[key] lookup per member. Enable with -DSIMDJSON_USE_KEY_SELECTOR_REFLECTION=1. namespace key_selector_reflection_detail { // A member participates if it is public, non-const, and not annotated to skip. consteval bool is_eligible_member(std::meta::info mem) { return !std::meta::is_const(mem) && std::meta::is_public(mem) && std::meta::annotations_of_with_type(mem, ^^simdjson::detail::skip_tag).empty(); } // JSON key name for `mem`, as a constevalutil::fixed_string usable as an NTTP. template consteval auto member_key_fixed_string() { constexpr std::string_view key{ simdjson::get_json_key_name() }; char buffer[key.size() + 1] = {}; for (std::size_t i = 0; i < key.size(); ++i) { buffer[i] = key[i]; } return constevalutil::fixed_string(buffer); } // key_selector template arguments (one fixed_string per eligible member), in // declaration order. template consteval std::vector selector_key_args() { std::vector args; template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { if constexpr (is_eligible_member(mem)) { args.push_back(std::meta::reflect_constant(member_key_fixed_string())); } } return args; } // key_selector whose keys are exactly T's eligible members (index i <-> i-th). template using selector_for = typename [: std::meta::substitute( ^^SIMDJSON_IMPLEMENTATION::ondemand::key_selector, selector_key_args()) :]; } // namespace key_selector_reflection_detail template requires(user_defined_type && std::is_class_v) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { SIMDJSON_IMPLEMENTATION::ondemand::object obj; if constexpr (std::is_same_v, SIMDJSON_IMPLEMENTATION::ondemand::object>) { obj = val; } else { SIMDJSON_TRY(val.get_object().get(obj)); } using selector = key_selector_reflection_detail::selector_for; std::array seen_member{}; // Single pass over the object: each field whose key matches a member yields its // selector index, which we map back to the corresponding member. The callback // returns an error_code so that a value-parse error (e.g. a type mismatch on a // matched field) is propagated by for_each instead of being silently dropped. error_code walk_error = obj.template for_each( [&](std::size_t matched_index, SIMDJSON_IMPLEMENTATION::ondemand::value field_value) -> error_code { std::size_t counter = 0; error_code field_error = SUCCESS; template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { if constexpr (key_selector_reflection_detail::is_eligible_member(mem)) { if (matched_index == counter) { seen_member[counter] = true; field_error = field_value.get(out.[:mem:]); } ++counter; } } return field_error; }); if (walk_error) { return walk_error; } // Required (non-optional) members must be present: a missing one is reported as // NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional members may be // absent. std::size_t check_counter = 0; template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { if constexpr (key_selector_reflection_detail::is_eligible_member(mem)) { if constexpr (!concepts::optional_type) { if (!seen_member[check_counter]) { return NO_SUCH_FIELD; } } ++check_counter; } } return SUCCESS; } #else template requires(user_defined_type && std::is_class_v) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { SIMDJSON_IMPLEMENTATION::ondemand::object obj; if constexpr (std::is_same_v, SIMDJSON_IMPLEMENTATION::ondemand::object>) { obj = val; } else { SIMDJSON_TRY(val.get_object().get(obj)); } template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem) && std::meta::annotations_of_with_type(mem, ^^simdjson::detail::skip_tag).empty()) { constexpr std::string_view key = simdjson::get_json_key_name(); if constexpr (concepts::optional_type) { // for optional members, it's ok if the key is missing auto error = obj[key].get(out.[:mem:]); if (error && error != NO_SUCH_FIELD) { if(error == NO_SUCH_FIELD) { out.[:mem:].reset(); continue; } return error; } } else { // for non-optional members, the key must be present SIMDJSON_TRY(obj[key].get(out.[:mem:])); } } }; return simdjson::SUCCESS; } #endif // SIMDJSON_USE_KEY_SELECTOR_REFLECTION // Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 template requires(std::is_enum_v) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { #if SIMDJSON_STATIC_REFLECTION std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); template for (constexpr auto enum_val : enumerators) { if (str == std::meta::identifier_of(enum_val)) { out = [:enum_val:]; return SUCCESS; } }; return INCORRECT_TYPE; #else // Fallback: deserialize as integer if reflection not available std::underlying_type_t int_val; SIMDJSON_TRY(val.get(int_val)); out = static_cast(int_val); return SUCCESS; #endif } template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } } if (auto err = val.get(*out)) { out.reset(); return err; } return SUCCESS; } template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } } if (auto err = val.get(*out)) { out.reset(); return err; } return SUCCESS; } #endif // SIMDJSON_STATIC_REFLECTION //////////////////////////////////////// // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_bool().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_int64().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_uint64().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_double().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_string().get(*out)); return SUCCESS; } //////////////////////////////////////// // Shared pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_bool().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_int64().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_uint64().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_double().get(*out)); return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); return SUCCESS; } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } } SIMDJSON_TRY(val.get_string().get(*out)); return SUCCESS; } //////////////////////////////////////// // Explicit optional specializations //////////////////////////////////////// //////////////////////////////////////// // Explicit smart pointer specializations for string and int types //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { // Check if the value is null bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); // Set to nullptr return SUCCESS; } if (!out) { out = std::make_unique(); } std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); *out = std::string{str}; return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { // Check if the value is null bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); // Set to nullptr return SUCCESS; } if (!out) { out = std::make_shared(); } std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); *out = std::string{str}; return SUCCESS; } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { // Check if the value is null bool is_null_value; SIMDJSON_TRY( val.is_null().get(is_null_value) ); if (is_null_value) { out.reset(); // Set to nullptr return SUCCESS; } if (!out) { out = std::make_unique(); } int64_t temp; SIMDJSON_TRY(val.get_int64().get(temp)); *out = static_cast(temp); return SUCCESS; } } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H #endif // SIMDJSON_SUPPORTS_CONCEPTS