From 420e9218cd41a64a17e261b5e3786dd02c1354f5 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 15 Oct 2026 11:46:01 +0200 Subject: [PATCH] Hint the end when converting an object to a standard container from_json for map-like containers called emplace() without a hint, which searches the tree for every key. The keys of the source object are sorted, so inserting with the end as the hint is constant time, as it was in 3.12.0 via std::inserter. get>() of a 25,000 entry object: 4.2 ms -> 3.25 ms (3.12.0: 3.7 ms). Signed-off-by: Niels Lohmann --- include/nlohmann/detail/conversions/from_json.hpp | 6 +++++- single_include/nlohmann/json.hpp | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index a2c82923c..b30249f49 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -397,9 +397,13 @@ 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> {}); + using value_type = typename ConstructibleObjectType::value_type; for (const auto& p : *inner_object) { - ret.emplace(p.first, p.second.template get()); + // the keys of the source are sorted for the usual object types, so + // hinting the end makes every insertion constant-time (for containers + // that order their keys the same way) + ret.insert(ret.end(), value_type(p.first, p.second.template get())); } obj = std::move(ret); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4585ab440..851a8006d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -6101,9 +6101,13 @@ 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> {}); + using value_type = typename ConstructibleObjectType::value_type; for (const auto& p : *inner_object) { - ret.emplace(p.first, p.second.template get()); + // the keys of the source are sorted for the usual object types, so + // hinting the end makes every insertion constant-time (for containers + // that order their keys the same way) + ret.insert(ret.end(), value_type(p.first, p.second.template get())); } obj = std::move(ret); }