From 083e1ce1395fbc80a17ed681b7e2becbc9118e3f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 19 Oct 2026 17:55:32 +0200 Subject: [PATCH] Address the review of editable json documents From gregmarr's review of #5630: - set() with a JSON pointer below a null value checks for "-" before scanning the token for digits. - node_flags::storage is defined as escaped | edited, and the comment on document_data::base says that the fourth slot is unused. - source_extent() names its root check. - The integer-index overloads of set(), insert(), and erase() use a named trait instead of a negated condition, which astyle formats like the library's other enable_ifs. Signed-off-by: Niels Lohmann --- .../nlohmann/detail/view/document_data.hpp | 2 +- include/nlohmann/detail/view/node.hpp | 2 +- include/nlohmann/detail/view/value.hpp | 4 ++++ include/nlohmann/json_view.hpp | 20 ++++++++++--------- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/nlohmann/detail/view/document_data.hpp b/include/nlohmann/detail/view/document_data.hpp index 9161f58ba..c36f2e835 100644 --- a/include/nlohmann/detail/view/document_data.hpp +++ b/include/nlohmann/detail/view/document_data.hpp @@ -54,7 +54,7 @@ struct document_data std::vector indexes; std::vector index_slots; std::vector large_objects; ///< positions of the objects to index (noted while parsing) - std::array base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena, edit arena (indexed by flags & node_flags::storage) + std::array base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena, edit arena, unused (indexed by flags & node_flags::storage) bool discarded = true; /// The storage of edits (editable documents only; see edit_storage.hpp). diff --git a/include/nlohmann/detail/view/node.hpp b/include/nlohmann/detail/view/node.hpp index cccc4eb4f..7a05c8e0a 100644 --- a/include/nlohmann/detail/view/node.hpp +++ b/include/nlohmann/detail/view/node.hpp @@ -34,7 +34,7 @@ struct node_flags { static constexpr std::uint8_t escaped = 1; ///< string payload lives in the decode arena, not the source static constexpr std::uint8_t edited = 2; ///< string or number token lives in the edit arena (editable documents) - static constexpr std::uint8_t storage = 3; ///< mask: where a string or number token lives (index into document_data::base) + static constexpr std::uint8_t storage = escaped | edited; ///< mask: where a string or number token lives (index into document_data::base) static constexpr std::uint8_t is_true = 4; ///< boolean value static constexpr std::uint8_t moved = 8; ///< array/object: the elements live in a separate sequence (editable documents) static constexpr std::uint8_t is_new = 16; ///< written by an edit: no source position diff --git a/include/nlohmann/detail/view/value.hpp b/include/nlohmann/detail/view/value.hpp index 6014017cf..1f1eafde4 100644 --- a/include/nlohmann/detail/view/value.hpp +++ b/include/nlohmann/detail/view/value.hpp @@ -114,6 +114,10 @@ struct is_non_bool_arithmetic : conjunction, negation struct is_non_c_string_default : negation::type, const char*>> {}; +/// the integral types accepted as an array index by the edits of a document +template +struct is_non_bool_integral : conjunction, negation>> {}; + /// whether View::get() converts to T without materialize(); get_to() /// assigns these, and passes the others to from_json template diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index e5dfb0c5f..da18cd698 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -747,8 +747,10 @@ class basic_json_view { if (editable() && m_doc->edits != nullptr) { - // positions of moved and new values are not source offsets - return m_node == m_doc->nodes ? m_doc->size + m_doc->edits->text_used : 64; + // positions of moved and new values are not source offsets; the root + // (the first node) spans the source text and every edit + const bool is_root = m_node == m_doc->nodes; + return is_root ? m_doc->size + m_doc->edits->text_used : 64; } const node* const end = m_doc->nodes + m_doc->node_count; if ((m_node->flags & detail::view::node_flags::storage) != 0) @@ -1129,7 +1131,7 @@ class basic_json_document } /// assign an existing array element; returns a view of it - template < typename I, typename V, typename std::enable_if < std::is_integral::value && !std::is_same::value, int >::type = 0 > + template::value, int>::type = 0> view_type set(view_type array, I idx, V && value) { return editor().set(array, index(idx), std::forward(value)); @@ -1151,14 +1153,14 @@ class basic_json_document const auto& token = ptr.back(); if (parent.is_null()) { - const bool digits = std::all_of(token.begin(), token.end(), [](const char c) - { - return c >= '0' && c <= '9'; - }); if (token == "-") { return push_back(parent, std::forward(value)); } + const bool digits = std::all_of(token.begin(), token.end(), [](const char c) + { + return c >= '0' && c <= '9'; + }); if (digits) { // (an invalid index is an error before the parent changes) @@ -1196,7 +1198,7 @@ class basic_json_document /// insert into an array before position idx (idx <= size()); returns a /// view of the new element - template < typename I, typename V, typename std::enable_if < std::is_integral::value && !std::is_same::value, int >::type = 0 > + template::value, int>::type = 0> view_type insert(view_type array, I idx, V && value) { return editor().insert(array, index(idx), std::forward(value)); @@ -1209,7 +1211,7 @@ class basic_json_document } /// remove an array element - template < typename I, typename std::enable_if < std::is_integral::value && !std::is_same::value, int >::type = 0 > + template::value, int>::type = 0> void erase(view_type array, I idx) { editor().erase(array, index(idx));