diff --git a/docs/mkdocs/docs/examples/get__ValueType_const.output b/docs/mkdocs/docs/examples/get__ValueType_const.output index 5cd9cd3aa..e7e9b5d59 100644 --- a/docs/mkdocs/docs/examples/get__ValueType_const.output +++ b/docs/mkdocs/docs/examples/get__ValueType_const.output @@ -4,8 +4,8 @@ Hello, world! 1 2 3 4 5 -string: "Hello, world!" number: {"floating-point":17.23,"integer":42} null: null +string: "Hello, world!" boolean: true array: [1,2,3,4,5] diff --git a/docs/mkdocs/docs/examples/get_to.output b/docs/mkdocs/docs/examples/get_to.output index 5cd9cd3aa..e7e9b5d59 100644 --- a/docs/mkdocs/docs/examples/get_to.output +++ b/docs/mkdocs/docs/examples/get_to.output @@ -4,8 +4,8 @@ Hello, world! 1 2 3 4 5 -string: "Hello, world!" number: {"floating-point":17.23,"integer":42} null: null +string: "Hello, world!" boolean: true array: [1,2,3,4,5] diff --git a/docs/mkdocs/docs/examples/operator__ValueType.output b/docs/mkdocs/docs/examples/operator__ValueType.output index a3bd9fff4..de471ec02 100644 --- a/docs/mkdocs/docs/examples/operator__ValueType.output +++ b/docs/mkdocs/docs/examples/operator__ValueType.output @@ -4,9 +4,9 @@ Hello, world! 1 2 3 4 5 -string: "Hello, world!" number: {"floating-point":17.23,"integer":42} null: null +string: "Hello, world!" boolean: true array: [1,2,3,4,5] [json.exception.type_error.302] type must be boolean, but is string diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 6856a0965..11e40f5f4 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -398,6 +398,17 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } +template +auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/) +-> decltype(obj.reserve(size), void()) +{ + obj.reserve(size); +} + +template +inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/) +{} + template::value, int> = 0> inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -409,6 +420,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); + from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {}); for (const auto& p : *inner_object) { ret.emplace(p.first, p.second.template get()); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0610eafee..d3c8293a7 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5693,6 +5693,17 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } +template +auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/) +-> decltype(obj.reserve(size), void()) +{ + obj.reserve(size); +} + +template +inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/) +{} + template::value, int> = 0> inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -5704,6 +5715,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); + from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {}); for (const auto& p : *inner_object) { ret.emplace(p.first, p.second.template get()); 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>();