diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index 82305dc58..3822c720c 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -2446,6 +2446,21 @@ inline const node* find_member(const document_data& d, const node* object, const return nullptr; } +/// the number of members of an object with this key (duplicates included) +inline std::size_t count_members(const document_data& d, const node* object, const char* key, std::size_t n) noexcept +{ + std::size_t result = 0; + const node* const end = document_data::child_end(object); + for (const node* m = document_data::first_child(object); m != end; m = document_data::after(m + 1)) + { + if (m->len == n && (n == 0 || std::memcmp(d.str(*m), key, n) == 0)) + { + ++result; + } + } + return result; +} + /// whether an integer type is accepted as an array index by the view's /// operator[] and at(): every integer type but bool and size_t, which has its /// own overload @@ -2768,24 +2783,24 @@ enum class index_status template index_status array_index(const StringType& s, std::size_t& idx) noexcept { + // as basic_json: the empty token and "-" (past the last element) are + // unresolved rather than not a number + if (s.empty() || (s.size() == 1 && s[0] == '-')) + { + return index_status::unresolved; + } + if (s[0] < '0' || s[0] > '9') + { + return index_status::not_number; + } if (s.size() > 1 && s[0] == '0') { return index_status::leading_zero; } - // as basic_json: also a single character, but "-" and the empty token are - // left to the conversion below and reported as unresolved - if (!s.empty() && !(s.size() == 1 && s[0] == '-') && !(s[0] >= '0' && s[0] <= '9')) - { - return index_status::not_number; - } - if (s.empty()) - { - return index_status::unresolved; - } std::uint64_t v = 0; - for (std::size_t i = 0; i < s.size(); ++i) + for (const auto c : s) { - const auto d = static_cast(static_cast(s[i])) - '0'; + const auto d = static_cast(static_cast(c)) - '0'; if (d > 9 || v > ((std::numeric_limits::max)() - d) / 10) { return index_status::unresolved; // not a number, or beyond unsigned long long @@ -3020,6 +3035,7 @@ NLOHMANN_JSON_NAMESPACE_END +#include // nullptr_t, size_t #include // int64_t #include // map #include // basic_string @@ -3120,6 +3136,27 @@ Map map_value(const View& v) template struct is_string_key : std::is_constructible {}; +/// whether View::get() converts to T without materialize(); get_to() +/// assigns these, and passes the others to from_json +template +struct is_direct_value : std::integral_constant < bool, + std::is_arithmetic::value || std::is_same::value + || std::is_same::value + || std::is_same::value || std::is_same::value > +{}; + +template +struct is_direct_value, BasicJsonType, View> : std::true_type {}; + +template +struct is_direct_value, BasicJsonType, View> : std::true_type {}; + +template +struct is_direct_value, BasicJsonType, View> : is_string_key {}; + +template +struct is_direct_value, BasicJsonType, View> : is_string_key {}; + } // namespace view } // namespace detail NLOHMANN_JSON_NAMESPACE_END @@ -3304,7 +3341,7 @@ class basic_json_view { if (is_discarded()) { - return basic_json_view(); + return {}; } detail::view::throw_type_error(305, "cannot use operator[] with a string argument with ", type_name()); } @@ -3330,7 +3367,7 @@ class basic_json_view { if (is_discarded()) { - return basic_json_view(); + return {}; } detail::view::throw_type_error(305, "cannot use operator[] with a numeric argument with ", type_name()); } @@ -3353,7 +3390,7 @@ class basic_json_view { if (NLOHMANN_VIEW_UNLIKELY(is_discarded())) { - return basic_json_view(); + return {}; } return detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::unchecked); } @@ -3526,11 +3563,11 @@ class basic_json_view return !detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::contains).is_discarded(); } - /// 1 if this is an object with a member with this key, else 0 (duplicate - /// keys count once) + /// the number of members with this key (more than 1 if the key occurs + /// more than once); 0 if this is not an object size_type count(string_view_t key) const { - return contains(key) ? 1 : 0; + return is_object() ? detail::view::count_members(*m_doc, m_node, key.data(), key.size()) : 0; } size_type count(const char* key) const @@ -3597,10 +3634,12 @@ class basic_json_view return get_impl(detail::view::value_tag {}, detail::priority_tag<2> {}); } + /// as BasicJsonType::get_to(): the types get() converts directly are + /// assigned, all others are passed to from_json with v as it is template T& get_to(T& v) const { - v = get(); + get_to_impl(v, detail::view::is_direct_value {}); return v; } @@ -3744,6 +3783,21 @@ class basic_json_view return materialize().template get(); } + // --- get_to() dispatch --- + + template + void get_to_impl(T& v, std::true_type /*direct*/) const + { + v = get(); + } + + /// from_json may keep parts of v (members a JSON object lacks, say) + template + void get_to_impl(T& v, std::false_type /*direct*/) const + { + materialize().get_to(v); + } + const document_data* m_doc = nullptr; const node* m_node = nullptr; };