From 0092111f4de1df733eb69b29b790083e0e8b3261 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 23 Oct 2026 10:24:50 +0200 Subject: [PATCH] 2.3.5: json_sax_dom_error_state base for the DOM SAX parsers (#5891) Holds errored, allow_exceptions, parse_error() and is_errored(). The scalar handlers stay in each class (parse hot path). Signed-off-by: Niels Lohmann --- include/nlohmann/detail/input/json_sax.hpp | 98 +++++++++++----------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 966610e4a..6a56b3453 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -315,6 +315,52 @@ struct diagnostic_positions }; #endif +/*! +@brief error state shared by the DOM-building SAX parsers + +Holds whether a parse error occurred and whether errors are thrown, and +implements the parse_error() and is_errored() members of the SAX interface for +json_sax_dom_parser and json_sax_dom_callback_parser. +*/ +class json_sax_dom_error_state +{ + public: + explicit json_sax_dom_error_state(const bool allow_exceptions_) noexcept + : allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_error_state(const json_sax_dom_error_state&) = delete; + json_sax_dom_error_state(json_sax_dom_error_state&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_error_state& operator=(const json_sax_dom_error_state&) = delete; + json_sax_dom_error_state& operator=(json_sax_dom_error_state&&) = delete; // the const member prevents assignment + ~json_sax_dom_error_state() = default; + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + /*! @brief SAX implementation to create a JSON value from SAX events @@ -329,7 +375,7 @@ constructor contains the parsed value. @tparam BasicJsonType the JSON type */ template -class json_sax_dom_parser +class json_sax_dom_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -345,7 +391,7 @@ class json_sax_dom_parser @param[in] allow_exceptions_ whether parse errors yield exceptions */ explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_) {} // make class move-only @@ -474,24 +520,6 @@ class json_sax_dom_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: /*! @@ -545,16 +573,12 @@ class json_sax_dom_parser std::vector ref_stack {}; /// helper to hold the reference for the next object element BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// the lexer reference to obtain the current position lexer_t* m_lexer_ref = nullptr; }; template -class json_sax_dom_callback_parser +class json_sax_dom_callback_parser : public json_sax_dom_error_state { public: using number_integer_t = typename BasicJsonType::number_integer_t; @@ -570,7 +594,7 @@ class json_sax_dom_callback_parser parser_callback_t cb, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) - : root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + : json_sax_dom_error_state(allow_exceptions_), root(r), callback(std::move(cb)), m_lexer_ref(lexer_) { keep_stack.push_back(true); } @@ -837,24 +861,6 @@ class json_sax_dom_callback_parser return true; } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - private: /// if there is a pending duplicate-key stash entry for this exact slot, @@ -1096,12 +1102,8 @@ class json_sax_dom_callback_parser /// value is later rejected by the callback, instead of erasing the /// member entirely std::vector> duplicate_key_stash {}; - /// whether a syntax error occurred - bool errored = false; /// callback function const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; /// a discarded value for the callback BasicJsonType discarded = BasicJsonType::value_t::discarded; /// the lexer reference to obtain the current position