mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 19:50:34 +00:00
Keep JSON_DIAGNOSTICS parent pointers of ordered_json members after erase() and update() (#5552)
* Keep JSON_DIAGNOSTICS parent pointers of ordered_json members after erase() and update() ordered_json stores its members in a vector, and two operations moved members without restoring their parent pointers afterwards: - ordered_map::erase() re-constructs every member after the erased one in place. The basic_json move constructor leaves m_parent at nullptr, and none of the object branches of basic_json::erase() (by key, iterator, or iterator range) called set_parents(). This also affected merge_patch() with a null member and patch() with a remove operation. - update() only set the parent pointer of the inserted member. Adding a key can reallocate the vector, which copies all other members and leaves their m_parent at nullptr. The set_parents() call added for #4813 only repaired this for the nested object of a merge, not for the target. The next assert_invariant() on such an object (for instance, when copying it) aborted, and diagnostic messages lost the path prefix above the moved member. std::map-based json was not affected, because its nodes do not move. Erasing from an ordered_map object now calls set_parents(), and update() uses set_parent(), which already refreshes all members for vector-based objects. This makes the #4813 workaround redundant. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Account for JSON_DIAGNOSTIC_POSITIONS in the ordered_json parent-pointer test The merge_patch() case parses its input, so with JSON_DIAGNOSTIC_POSITIONS the exception message also carries the byte range of the parsed value. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence clang-tidy for the intentional copy in the ordered_json parent-pointer test Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep parent pointers when update() merges past its descent bound The iterative path of update() only set the parent pointer of the member it inserted, like the recursive one did before. It now uses set_parent() too, so ordered_json members that move when a nested object grows keep their parents, and the set_parents() calls that patched this up after each nested merge are gone. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -25701,6 +25701,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
|
||||
|
||||
/// @brief restore the parent pointers after erasing from an object
|
||||
/// ordered_json keeps its members in a vector, and erasing a member
|
||||
/// re-constructs every member after it in place, which resets their
|
||||
/// parent pointers
|
||||
void set_parents_after_object_erase()
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
#ifdef JSON_HEDLEY_MSVC_VERSION
|
||||
#pragma warning(push )
|
||||
#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
|
||||
#endif
|
||||
if (detail::is_ordered_map<object_t>::value)
|
||||
{
|
||||
set_parents();
|
||||
}
|
||||
#ifdef JSON_HEDLEY_MSVC_VERSION
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
//////////////////////////
|
||||
// JSON parser callback //
|
||||
@@ -27389,6 +27410,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
case value_t::object:
|
||||
{
|
||||
result.m_it.object_iterator = erase_from_object(pos.m_it.object_iterator);
|
||||
set_parents_after_object_erase();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27461,6 +27483,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
{
|
||||
result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator,
|
||||
last.m_it.object_iterator);
|
||||
set_parents_after_object_erase();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27491,7 +27514,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
||||
}
|
||||
|
||||
return m_data.m_value.object->erase(std::forward<KeyType>(key));
|
||||
const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key));
|
||||
set_parents_after_object_erase();
|
||||
return erased;
|
||||
}
|
||||
|
||||
template < typename KeyType, detail::enable_if_t <
|
||||
@@ -27508,6 +27533,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
if (it != m_data.m_value.object->end())
|
||||
{
|
||||
m_data.m_value.object->erase(it);
|
||||
set_parents_after_object_erase();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -28419,16 +28445,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
if (it2 != m_data.m_value.object->end() && it2->second.is_object())
|
||||
{
|
||||
it2->second.update_members(it.value().cbegin(), it.value().cend(), true, depth + 1);
|
||||
#if JSON_DIAGNOSTICS
|
||||
it2->second.set_parents();
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
m_data.m_value.object->operator[](it.key()) = it.value();
|
||||
#if JSON_DIAGNOSTICS
|
||||
m_data.m_value.object->operator[](it.key()).m_parent = this;
|
||||
#endif
|
||||
// set_parent() also repairs the other members, which ordered_json
|
||||
// relocates when adding a key makes its vector grow
|
||||
set_parent(m_data.m_value.object->operator[](it.key()) = it.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28457,9 +28479,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
|
||||
// a nested object is merged: continue with its parent
|
||||
#if JSON_DIAGNOSTICS
|
||||
target->set_parents();
|
||||
#endif
|
||||
target = stack.back().target;
|
||||
first = stack.back().position;
|
||||
last = stack.back().last;
|
||||
@@ -28481,10 +28500,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
continue;
|
||||
}
|
||||
}
|
||||
target->m_data.m_value.object->operator[](first.key()) = first.value();
|
||||
#if JSON_DIAGNOSTICS
|
||||
target->m_data.m_value.object->operator[](first.key()).m_parent = target;
|
||||
#endif
|
||||
// set_parent() also repairs the other members, which ordered_json
|
||||
// relocates when adding a key makes its vector grow
|
||||
target->set_parent(target->m_data.m_value.object->operator[](first.key()) = first.value());
|
||||
++first;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user