diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 6ebc095bf..7b5d494f8 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -389,15 +389,15 @@ class parser // states.back() is false -> object - // comma -> next value - if (get_token() == token_type::value_separator) + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) { - // parse key - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) @@ -405,12 +405,12 @@ class parser return false; } - // parse separator (:) - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); } // parse values @@ -436,9 +436,9 @@ class parser continue; } - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); } } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 30dd96f43..605cc699a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -73,35 +73,93 @@ SOFTWARE. #include // runtime_error #include // to_string -// #include +// #include +#include // array #include // size_t +#include // uint8_t +#include // string namespace nlohmann { namespace detail { -/// struct to capture the start position of the current token -struct position_t -{ - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; +/////////////////////////// +// JSON type enumeration // +/////////////////////////// - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const - { - return chars_read_total; - } +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function }; -} // namespace detail -} // namespace nlohmann +/*! +@brief comparison operator for JSON types +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // #include @@ -2380,6 +2438,97 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + namespace nlohmann { namespace detail @@ -2438,6 +2587,61 @@ class exception : public std::exception return "[json.exception." + ename + "." + std::to_string(id_) + "] "; } + template + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + return ""; +#endif + } + private: /// an exception object as storage for error messages std::runtime_error m; @@ -2500,18 +2704,20 @@ class parse_error : public exception @param[in] what_arg the explanatory string @return parse_error object */ - static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + what_arg; + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; return parse_error(id_, pos.chars_read_total, w.c_str()); } - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("parse_error", id_) + "parse error" + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + what_arg; + ": " + exception::diagnostics(context) + what_arg; return parse_error(id_, byte_, w.c_str()); } @@ -2577,9 +2783,10 @@ caught.,invalid_iterator} class invalid_iterator : public exception { public: - static invalid_iterator create(int id_, const std::string& what_arg) + template + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) { - std::string w = exception::name("invalid_iterator", id_) + what_arg; + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; return invalid_iterator(id_, w.c_str()); } @@ -2631,9 +2838,10 @@ caught.,type_error} class type_error : public exception { public: - static type_error create(int id_, const std::string& what_arg) + template + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) { - std::string w = exception::name("type_error", id_) + what_arg; + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; return type_error(id_, w.c_str()); } @@ -2678,9 +2886,10 @@ caught.,out_of_range} class out_of_range : public exception { public: - static out_of_range create(int id_, const std::string& what_arg) + template + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) { - std::string w = exception::name("out_of_range", id_) + what_arg; + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; return out_of_range(id_, w.c_str()); } @@ -2716,9 +2925,10 @@ caught.,other_error} class other_error : public exception { public: - static other_error create(int id_, const std::string& what_arg) + template + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) { - std::string w = exception::name("other_error", id_) + what_arg; + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; return other_error(id_, w.c_str()); } @@ -3423,87 +3633,6 @@ struct is_constructible_tuple> : conjunction -#include // array -#include // size_t -#include // uint8_t -#include // string - -namespace nlohmann -{ -namespace detail -{ -/////////////////////////// -// JSON type enumeration // -/////////////////////////// - -/*! -@brief the JSON type enumeration - -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. - -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. - -@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type - -@since version 1.0.0 -*/ -enum class value_t : std::uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function -}; - -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string < binary -- furthermore, each type is not smaller than itself -- discarded values are not comparable -- binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - -@since version 1.0.0 -*/ -inline bool operator<(const value_t lhs, const value_t rhs) noexcept -{ - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; -} -} // namespace detail -} // namespace nlohmann - - namespace nlohmann { namespace detail @@ -3513,7 +3642,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); } n = nullptr; } @@ -3544,7 +3673,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); } } @@ -3553,7 +3682,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); } b = *j.template get_ptr(); } @@ -3563,7 +3692,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } s = *j.template get_ptr(); } @@ -3579,7 +3708,7 @@ void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } s = *j.template get_ptr(); @@ -3619,7 +3748,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } l.clear(); std::transform(j.rbegin(), j.rend(), @@ -3636,7 +3765,7 @@ void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), @@ -3727,8 +3856,7 @@ void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + - std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } from_json_array_impl(j, arr, priority_tag<3> {}); @@ -3739,7 +3867,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); } bin = *j.template get_ptr(); @@ -3751,7 +3879,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); } ConstructibleObjectType ret; @@ -3805,7 +3933,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); } } @@ -3834,14 +3962,14 @@ void from_json(const BasicJsonType& j, std::map& { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -3854,14 +3982,14 @@ void from_json(const BasicJsonType& j, std::unordered_map(), p.at(1).template get()); } @@ -4207,6 +4335,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = arr; + j.set_parents(); j.assert_invariant(); } @@ -4215,6 +4344,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = std::move(arr); + j.set_parents(); j.assert_invariant(); } @@ -4227,6 +4357,7 @@ struct external_constructor using std::end; j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parents(); j.assert_invariant(); } @@ -4239,6 +4370,7 @@ struct external_constructor for (const bool x : arr) { j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); } j.assert_invariant(); } @@ -4254,6 +4386,7 @@ struct external_constructor { std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); } + j.set_parents(); j.assert_invariant(); } }; @@ -4266,6 +4399,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = obj; + j.set_parents(); j.assert_invariant(); } @@ -4274,6 +4408,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = std::move(obj); + j.set_parents(); j.assert_invariant(); } @@ -4286,6 +4421,7 @@ struct external_constructor j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parents(); j.assert_invariant(); } }; @@ -5509,8 +5645,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, - "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); } return true; @@ -5525,6 +5660,7 @@ class json_sax_dom_parser bool end_object() { + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } @@ -5535,8 +5671,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, - "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); } return true; @@ -5544,6 +5679,7 @@ class json_sax_dom_parser bool end_array() { + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } @@ -5690,7 +5826,7 @@ class json_sax_dom_callback_parser // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); } return true; @@ -5715,10 +5851,17 @@ class json_sax_dom_callback_parser bool end_object() { - if (ref_stack.back() && !callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + if (ref_stack.back()) { - // discard object - *ref_stack.back() = discarded; + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parents(); + } } JSON_ASSERT(!ref_stack.empty()); @@ -5753,7 +5896,7 @@ class json_sax_dom_callback_parser // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); } return true; @@ -5766,7 +5909,11 @@ class json_sax_dom_callback_parser if (ref_stack.back()) { keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (!keep) + if (keep) + { + ref_stack.back()->set_parents(); + } + else { // discard array *ref_stack.back() = discarded; @@ -5864,7 +6011,7 @@ class json_sax_dom_callback_parser // array if (ref_stack.back()->is_array()) { - ref_stack.back()->m_value.array->push_back(std::move(value)); + ref_stack.back()->m_value.array->emplace_back(std::move(value)); return {true, &(ref_stack.back()->m_value.array->back())}; } @@ -7886,7 +8033,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error(chars_read, get_token_string(), - parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); } } @@ -7962,7 +8109,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 1)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); } return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); @@ -7983,7 +8130,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 0)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); } // All BSON binary values have a subtype @@ -8065,7 +8212,7 @@ class binary_reader { std::array cr{{}}; (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); - return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); } } } @@ -8465,7 +8612,7 @@ class binary_reader case cbor_tag_handler_t::error: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } case cbor_tag_handler_t::ignore: @@ -8580,7 +8727,7 @@ class binary_reader default: // anything else (0xFF is handled inside the other types) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } @@ -8675,7 +8822,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); } } } @@ -8774,7 +8921,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); } } } @@ -9241,7 +9388,7 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } @@ -9323,7 +9470,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); } } } @@ -9573,7 +9720,7 @@ class binary_reader default: auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); } } @@ -9643,7 +9790,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); } } } @@ -9681,7 +9828,7 @@ class binary_reader return false; } auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); } return get_ubjson_size_value(result.first); @@ -9771,7 +9918,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); } string_t s(1, static_cast(current)); return sax->string(s); @@ -9792,7 +9939,7 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } @@ -9970,7 +10117,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); } switch (result_number) @@ -9982,7 +10129,7 @@ class binary_reader case token_type::value_float: return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); default: - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); } } @@ -10138,7 +10285,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error(chars_read, "", - parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); } return true; } @@ -10311,7 +10458,6 @@ class parser { json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); sax_parse_internal(&sdp); - result.assert_invariant(); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) @@ -10319,7 +10465,7 @@ class parser sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"))); + exception_message(token_type::end_of_input, "value"), BasicJsonType())); } // in case of an error, return discarded value @@ -10340,15 +10486,13 @@ class parser { json_sax_dom_parser sdp(result, allow_exceptions); sax_parse_internal(&sdp); - result.assert_invariant(); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) { sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); } // in case of an error, return discarded value @@ -10358,6 +10502,8 @@ class parser return; } } + + result.assert_invariant(); } /*! @@ -10384,8 +10530,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); } return result; @@ -10431,8 +10576,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::value_string, "object key"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { @@ -10444,8 +10588,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::name_separator, "object separator"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); } // remember we are now inside an object @@ -10488,7 +10631,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); } if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) @@ -10558,16 +10701,14 @@ class parser // using "uninitialized" to avoid "expected" message return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::uninitialized, "value"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); } default: // the last token was unexpected { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::literal_or_value, "value"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); } } } @@ -10613,8 +10754,7 @@ class parser return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_array, "array"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); } // states.back() is false -> object @@ -10627,8 +10767,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::value_string, "object key"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) @@ -10641,8 +10780,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::name_separator, "object separator"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); } // parse values @@ -10670,8 +10808,7 @@ class parser return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_object, "object"))); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); } } @@ -11142,7 +11279,7 @@ class iter_impl } case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); default: { @@ -11151,7 +11288,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); } } } @@ -11185,7 +11322,7 @@ class iter_impl return m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); } } } @@ -11286,7 +11423,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); } JSON_ASSERT(m_object != nullptr); @@ -11323,7 +11460,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); } JSON_ASSERT(m_object != nullptr); @@ -11331,7 +11468,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -11379,7 +11516,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); case value_t::array: { @@ -11450,7 +11587,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -11471,13 +11608,13 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); default: { @@ -11486,7 +11623,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); } } } @@ -11504,7 +11641,7 @@ class iter_impl return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); } /*! @@ -11665,6 +11802,8 @@ class json_reverse_iterator : public std::reverse_iterator // #include +// #include + // #include @@ -11723,7 +11862,7 @@ class json_pointer std::string{}, [](const std::string & a, const std::string & b) { - return a + "/" + escape(b); + return a + "/" + detail::escape(b); }); } @@ -11903,7 +12042,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); } reference_tokens.pop_back(); @@ -11927,7 +12066,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); } return reference_tokens.back(); @@ -11993,15 +12132,13 @@ class json_pointer // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + s + - "' must not begin with '0'")); + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); } std::size_t processed_chars = 0; @@ -12012,20 +12149,20 @@ class json_pointer } JSON_CATCH(std::out_of_range&) { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); } // check if the string was completely read if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); } // only triggered on special platforms (like 32bit), see also // https://github.com/nlohmann/json/pull/2203 if (res >= static_cast((std::numeric_limits::max)())) { - JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE } return static_cast(res); @@ -12036,7 +12173,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); } json_pointer result = *this; @@ -12099,7 +12236,7 @@ class json_pointer single value; that is, with an empty list of reference tokens. */ default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); } } @@ -12171,7 +12308,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } @@ -12204,7 +12341,7 @@ class json_pointer // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + ") is out of range", *ptr)); } // note: at performs range check @@ -12213,7 +12350,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } @@ -12251,9 +12388,7 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); } // use unchecked array access @@ -12262,7 +12397,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } @@ -12295,7 +12430,7 @@ class json_pointer // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + ") is out of range", *ptr)); } // note: at performs range check @@ -12304,7 +12439,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } @@ -12408,9 +12543,7 @@ class json_pointer // check if nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { - JSON_THROW(detail::parse_error::create(107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); } // extract the reference tokens: @@ -12445,58 +12578,18 @@ class json_pointer (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) { - JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); } } // finally, store the reference token - unescape(reference_token); + detail::unescape(reference_token); result.push_back(reference_token); } return result; } - /*! - @brief replace all occurrences of a substring by another string - - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f - - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** - - @since version 2.0.0 - */ - static void replace_substring(std::string& s, const std::string& f, - const std::string& t) - { - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} - } - - JSON_PRIVATE_UNLESS_TESTED: - /// escape "~" to "~0" and "/" to "~1" - static std::string escape(std::string s) - { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape "~1" to tilde and "~0" to slash (order is important!) - static void unescape(std::string& s) - { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); - } - private: /*! @param[in] reference_string the reference string to the current value @@ -12542,7 +12635,7 @@ class json_pointer // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { - flatten(reference_string + "/" + escape(element.first), element.second, result); + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); } } break; @@ -12572,7 +12665,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); } BasicJsonType result; @@ -12582,7 +12675,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); } // assign value to reference pointed to by JSON pointer; Note that if @@ -12898,7 +12991,7 @@ class binary_writer default: { - JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j));; } } } @@ -13742,13 +13835,12 @@ class binary_writer @return The size of a BSON document entry header, including the id marker and the entry name size (and its null-terminator). */ - static std::size_t calc_bson_entry_header_size(const string_t& name) + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) { const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, - "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); } return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; @@ -13858,21 +13950,21 @@ class binary_writer @brief Writes a BSON element with key @a name and unsigned @a value */ void write_bson_unsigned(const string_t& name, - const std::uint64_t value) + const BasicJsonType& j) { - if (value <= static_cast((std::numeric_limits::max)())) + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x10 /* int32 */); - write_number(static_cast(value)); + write_number(static_cast(j.m_value.number_unsigned)); } - else if (value <= static_cast((std::numeric_limits::max)())) + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x12 /* int64 */); - write_number(static_cast(value)); + write_number(static_cast(j.m_value.number_unsigned)); } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64")); + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); } } @@ -13949,7 +14041,7 @@ class binary_writer static std::size_t calc_bson_element_size(const string_t& name, const BasicJsonType& j) { - const auto header_size = calc_bson_entry_header_size(name); + const auto header_size = calc_bson_entry_header_size(name, j); switch (j.type()) { case value_t::object: @@ -14018,7 +14110,7 @@ class binary_writer return write_bson_integer(name, j.m_value.number_integer); case value_t::number_unsigned: - return write_bson_unsigned(name, j.m_value.number_unsigned); + return write_bson_unsigned(name, j); case value_t::string: return write_bson_string(name, *j.m_value.string); @@ -16050,7 +16142,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn)); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType())); } case error_handler_t::ignore: @@ -16144,7 +16236,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType())); } case error_handler_t::ignore: @@ -16809,6 +16901,7 @@ class basic_json friend class ::nlohmann::detail::json_sax_dom_parser; template friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; @@ -17680,7 +17773,7 @@ class basic_json object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1", basic_json())); // LCOV_EXCL_LINE } break; } @@ -17847,13 +17940,83 @@ class basic_json invariant. Furthermore, it has to be called each time the type of a JSON value is changed, because the invariant expresses a relationship between @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. */ - void assert_invariant() const noexcept + void assert_invariant(bool check_parents = true) const noexcept { JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); +#else + static_cast(check_parents); +#endif + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count); +#endif + return it; + } + + reference set_parent(reference j) + { +#if JSON_DIAGNOSTICS + j.m_parent = this; +#else + static_cast(j); +#endif + return j; } public: @@ -18070,6 +18233,7 @@ class basic_json std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); assert_invariant(); } @@ -18148,6 +18312,7 @@ class basic_json default: // LCOV_EXCL_LINE JSON_ASSERT(false); // LCOV_EXCL_LINE } + set_parents(); assert_invariant(); } @@ -18249,7 +18414,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); } } @@ -18259,13 +18424,13 @@ class basic_json m_type = value_t::object; m_value = value_t::object; - std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) + for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); - }); + } } else { @@ -18274,6 +18439,7 @@ class basic_json m_value.array = create(init.begin(), init.end()); } + set_parents(); assert_invariant(); } @@ -18483,6 +18649,7 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); + set_parents(); assert_invariant(); } @@ -18552,7 +18719,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); } // copy type from first iterator @@ -18570,7 +18737,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); } break; } @@ -18632,10 +18799,10 @@ class basic_json } default: - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - std::string(first.m_object->type_name()))); + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); } + set_parents(); assert_invariant(); } @@ -18734,6 +18901,7 @@ class basic_json break; } + set_parents(); assert_invariant(); } @@ -18768,12 +18936,13 @@ class basic_json m_value(std::move(other.m_value)) { // check that passed value is valid - other.assert_invariant(); + other.assert_invariant(false); // invalidate payload other.m_type = value_t::null; other.m_value = {}; + set_parents(); assert_invariant(); } @@ -18814,6 +18983,7 @@ class basic_json swap(m_type, other.m_type); swap(m_value, other.m_value); + set_parents(); assert_invariant(); return *this; } @@ -18835,7 +19005,7 @@ class basic_json */ ~basic_json() noexcept { - assert_invariant(); + assert_invariant(false); m_value.destroy(m_type); } @@ -19328,7 +19498,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); } /// get a pointer to the value (object) @@ -19449,7 +19619,7 @@ class basic_json return *ptr; } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); } public: @@ -19877,7 +20047,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); } return *get_ptr(); @@ -19888,7 +20058,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); } return *get_ptr(); @@ -19938,17 +20108,17 @@ class basic_json { JSON_TRY { - return m_value.array->at(idx); + return set_parent(m_value.array->at(idx)); } JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } @@ -19990,12 +20160,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } @@ -20036,17 +20206,17 @@ class basic_json { JSON_TRY { - return m_value.object->at(key); + return set_parent(m_value.object->at(key)); } JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } @@ -20092,12 +20262,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } @@ -20142,15 +20312,22 @@ class basic_json // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { - m_value.array->insert(m_value.array->end(), - idx - m_value.array->size() + 1, - basic_json()); +#if JSON_DIAGNOSTICS + // remember array size before resizing + const auto previous_size = m_value.array->size(); +#endif + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + // set parent for values added above + set_parents(begin() + static_cast(previous_size), static_cast(idx + 1 - previous_size)); +#endif } return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); } /*! @@ -20180,7 +20357,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); } /*! @@ -20223,10 +20400,10 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return m_value.object->operator[](key); + return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @@ -20268,7 +20445,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @@ -20313,10 +20490,10 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return m_value.object->operator[](key); + return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @@ -20360,7 +20537,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @@ -20432,7 +20609,7 @@ class basic_json return default_value; } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); } /*! @@ -20505,7 +20682,7 @@ class basic_json } } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); } /*! @@ -20659,7 +20836,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } IteratorType result = end(); @@ -20675,7 +20852,7 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); } if (is_string()) @@ -20711,7 +20888,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } return result; @@ -20772,7 +20949,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); } IteratorType result = end(); @@ -20789,7 +20966,7 @@ class basic_json if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); } if (is_string()) @@ -20827,7 +21004,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } return result; @@ -20870,7 +21047,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } /*! @@ -20904,14 +21081,14 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } } @@ -21856,7 +22033,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an array @@ -21869,6 +22046,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); + set_parent(m_value.array->back()); // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -21891,7 +22069,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an array @@ -21904,6 +22082,7 @@ class basic_json // add element to array m_value.array->push_back(val); + set_parent(m_value.array->back()); } /*! @@ -21941,7 +22120,7 @@ class basic_json // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an object @@ -21952,8 +22131,9 @@ class basic_json assert_invariant(); } - // add element to array - m_value.object->insert(val); + // add element to object + auto res = m_value.object->insert(val); + set_parent(res.first->second); } /*! @@ -22044,7 +22224,7 @@ class basic_json // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); } // transform null object into an array @@ -22057,10 +22237,10 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 - return m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_value.array->emplace_back(std::forward(args)...)); #else m_value.array->emplace_back(std::forward(args)...); - return m_value.array->back(); + return set_parent(m_value.array->back()); #endif } @@ -22097,7 +22277,7 @@ class basic_json // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); } // transform null object into an object @@ -22110,6 +22290,8 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -22168,14 +22350,14 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator - return insert_iterator(pos, val); + return set_parents(insert_iterator(pos, val), static_cast(1)); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } /*! @@ -22219,14 +22401,14 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator - return insert_iterator(pos, cnt, val); + return set_parents(insert_iterator(pos, cnt, val), static_cast(cnt)); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } /*! @@ -22264,28 +22446,28 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); } // insert to array and return iterator - return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + return set_parents(insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator), std::distance(first, last)); } /*! @@ -22317,17 +22499,17 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator - return insert_iterator(pos, ilist.begin(), ilist.end()); + return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast(ilist.size())); } /*! @@ -22358,19 +22540,19 @@ class basic_json // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); @@ -22407,11 +22589,11 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); } if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this)); } for (auto it = j.cbegin(); it != j.cend(); ++it) @@ -22458,20 +22640,20 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() || !last.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); } for (auto it = first; it != last; ++it) @@ -22506,6 +22688,9 @@ class basic_json { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + + set_parents(); + other.set_parents(); assert_invariant(); } @@ -22566,7 +22751,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } @@ -22599,7 +22784,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } @@ -22632,7 +22817,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } @@ -22665,7 +22850,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } @@ -22679,7 +22864,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } @@ -23587,6 +23772,11 @@ class basic_json /// the value of the current element json_value m_value = {}; +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// @@ -24883,7 +25073,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); } // default case: insert add offset @@ -24899,7 +25089,7 @@ class basic_json }; // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [&result](json_pointer & ptr) + const auto operation_remove = [this, &result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); @@ -24917,7 +25107,7 @@ class basic_json } else { - JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); } } else if (parent.is_array()) @@ -24930,7 +25120,7 @@ class basic_json // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); } // iterate and apply the operations @@ -24950,13 +25140,13 @@ class basic_json // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); } // no error: return value @@ -24966,7 +25156,7 @@ class basic_json // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); } // collect mandatory members @@ -25044,7 +25234,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); } break; @@ -25054,7 +25244,7 @@ class basic_json { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); } } } @@ -25170,7 +25360,7 @@ class basic_json for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); + const auto key = detail::escape(it.key()); if (target.find(it.key()) != target.end()) { @@ -25194,7 +25384,7 @@ class basic_json if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); + const auto key = detail::escape(it.key()); result.push_back( { {"op", "add"}, {"path", path + "/" + key},