Make flatten() linear for ordered_json

json_pointer::flatten inserted every leaf with operator[], which searches
ordered_map linearly, so flatten() was quadratic (citm_catalog.json: 265 ms
vs 5 ms for json). Flattened paths are unique by construction, so
ordered_map gets emplace_new_key() to append a key known to be absent;
other object types still use operator[]. Parent pointers are set once at
the end for JSON_DIAGNOSTICS.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-14 22:18:59 +02:00
1 parent 4e294f4b18
commit d60bd1dfc3
4 files changed
+126 -8

No files matched your search

+26 -4
View File
@@ -938,6 +938,23 @@ class json_pointer
}
private:
// Add the entry (key, v) to the object `obj`. Flattened paths are pairwise
// distinct, so objects that can append a key known to be absent
// (ordered_map) skip the lookup, which is linear and would make flatten
// quadratic; all other object types use operator[].
template<typename ObjectType, typename ValueType>
static auto add_flattened(ObjectType& obj, const string_t& key, ValueType&& v, int /*unused*/)
-> decltype(obj.emplace_new_key(key, std::forward<ValueType>(v)), void())
{
obj.emplace_new_key(key, std::forward<ValueType>(v));
}
template<typename ObjectType, typename ValueType>
static void add_flattened(ObjectType& obj, const string_t& key, ValueType&& v, ...)
{
obj[key] = std::forward<ValueType>(v);
}
/*!
@param[in] reference_string the reference string to the current value
@param[in] value the value to consider
@@ -982,7 +999,8 @@ class json_pointer
// flatten `v`, whose path is `path`: primitives and empty containers
// are added to the result right away; other containers get a frame
const auto enter = [&stack, &path, &result](const BasicJsonType & v)
auto& object = *result.m_data.m_value.object;
const auto enter = [&stack, &path, &object](const BasicJsonType & v)
{
switch (v.type())
{
@@ -991,7 +1009,7 @@ class json_pointer
if (v.m_data.m_value.array->empty())
{
// flatten empty array as null
result[path] = nullptr;
add_flattened(object, path, nullptr, 0);
}
else
{
@@ -1005,7 +1023,7 @@ class json_pointer
if (v.m_data.m_value.object->empty())
{
// flatten empty object as null
result[path] = nullptr;
add_flattened(object, path, nullptr, 0);
}
else
{
@@ -1025,7 +1043,7 @@ class json_pointer
default:
{
// add a primitive value with its reference string
result[path] = v;
add_flattened(object, path, v, 0);
return;
}
}
@@ -1071,6 +1089,10 @@ class json_pointer
enter(it->second);
}
}
// the entries were added without going through basic_json, so the
// parent pointers (JSON_DIAGNOSTICS) have to be set now
result.set_parents();
}
/*!