#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" #endif // SIMDJSON_CONDITIONAL_INCLUDE #include #include #if SIMDJSON_STATIC_REFLECTION #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); 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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); 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; } // 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)); 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