diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 6856a0965..fa5eb62b0 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -398,6 +398,34 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } +template +auto from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<1> /*unused*/) +-> decltype( + obj.reserve(std::declval()), + void()) +{ + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + ret.reserve(inner_object->size()); + for (const auto& p : *inner_object) + { + ret.emplace(p.first, p.second.template get()); + } + obj = std::move(ret); +} + +template +inline void from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<0> /*unused*/) +{ + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + for (const auto& p : *inner_object) + { + ret.emplace(p.first, p.second.template get()); + } + obj = std::move(ret); +} + template::value, int> = 0> inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -407,13 +435,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); } - ConstructibleObjectType ret; - const auto* inner_object = j.template get_ptr(); - for (const auto& p : *inner_object) - { - ret.emplace(p.first, p.second.template get()); - } - obj = std::move(ret); + from_json_object_impl(j, obj, priority_tag<1> {}); } // overload for arithmetic types, not chosen for basic_json template arguments diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 31bfb869d..4e484d764 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5693,6 +5693,34 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } +template +auto from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<1> /*unused*/) +-> decltype( + obj.reserve(std::declval()), + void()) +{ + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + ret.reserve(inner_object->size()); + for (const auto& p : *inner_object) + { + ret.emplace(p.first, p.second.template get()); + } + obj = std::move(ret); +} + +template +inline void from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& obj, priority_tag<0> /*unused*/) +{ + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + for (const auto& p : *inner_object) + { + ret.emplace(p.first, p.second.template get()); + } + obj = std::move(ret); +} + template::value, int> = 0> inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -5702,13 +5730,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); } - ConstructibleObjectType ret; - const auto* inner_object = j.template get_ptr(); - for (const auto& p : *inner_object) - { - ret.emplace(p.first, p.second.template get()); - } - obj = std::move(ret); + from_json_object_impl(j, obj, priority_tag<1> {}); } // overload for arithmetic types, not chosen for basic_json template arguments diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 1937affbb..4975854c0 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1389,6 +1389,37 @@ TEST_CASE("value conversion") // CHECK(m5["one"] == "eins"); } + SECTION("reserve is called on containers that support it (#5406)") + { + // build a larger object so that a missing/incorrect reserve() + // call would be more likely to corrupt or drop elements + json j_large; + for (int i = 0; i < 100; ++i) + { + j_large[std::to_string(i)] = i; + } + + SECTION("std::unordered_map (supports reserve)") + { + const auto m = j_large.get>(); + CHECK(m.size() == 100); + for (int i = 0; i < 100; ++i) + { + CHECK(m.at(std::to_string(i)) == i); + } + } + + SECTION("std::map (no reserve, fallback path)") + { + const auto m = j_large.get>(); + CHECK(m.size() == 100); + for (int i = 0; i < 100; ++i) + { + CHECK(m.at(std::to_string(i)) == i); + } + } + } + SECTION("std::multimap") { j1.get>();