diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 8c9bef03d..a827b037b 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1642,6 +1642,9 @@ class basic_json std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); +#ifdef JSON_DIAGNOSTICS + (*element.m_value.array)[1].m_parent = this; +#endif m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); @@ -1652,6 +1655,12 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); +#ifdef JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif } assert_invariant(); @@ -2696,6 +2705,49 @@ class basic_json /// @} private: +#ifdef JSON_DIAGNOSTICS + std::string diagnostics() + { + std::string result; + for (basic_json* current = this; 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 (current->m_parent->m_value.array->operator[](i) == *current) + { + result = "/" + std::to_string(i) + result; + continue; + } + } + break; + } + + case value_t::object: + { + for (auto it : *current->m_parent->m_value.object) + { + if (it.second == *current) + { + result = "/" + it.first + result; + continue; + } + } + break; + } + + default: + break; + } + } + + return result; + } +#endif + ////////////////// // value access // ////////////////// @@ -3318,7 +3370,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->at(idx); + result.m_parent = this; + return result; +#else return m_value.array->at(idx); +#endif } JSON_CATCH (std::out_of_range&) { @@ -3416,7 +3474,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->at(key); + result.m_parent = this; + return result; +#else return m_value.object->at(key); +#endif } JSON_CATCH (std::out_of_range&) { @@ -3525,9 +3589,18 @@ class basic_json m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->operator[](idx); + result.m_parent = this; + return result; +#else return m_value.array->operator[](idx); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); @@ -3603,7 +3676,13 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -3693,7 +3772,13 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -5249,6 +5334,9 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -5284,6 +5372,9 @@ class basic_json // add element to array m_value.array->push_back(val); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } /*! @@ -5332,8 +5423,13 @@ class basic_json assert_invariant(); } - // add element to array + // add element to object +#ifdef JSON_DIAGNOSTICS + auto res = m_value.object->insert(val); + res.first->second.m_parent = this; +#else m_value.object->insert(val); +#endif } /*! @@ -5437,9 +5533,18 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->emplace_back(std::forward(args)...); + result.m_parent = this; + return result; +#else return m_value.array->emplace_back(std::forward(args)...); +#endif #else m_value.array->emplace_back(std::forward(args)...); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif return m_value.array->back(); #endif } @@ -6967,6 +7072,11 @@ class basic_json /// the value of the current element json_value m_value = {}; +#ifdef JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8b6344f92..f27a63ff2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18266,6 +18266,9 @@ class basic_json std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); +#ifdef JSON_DIAGNOSTICS + (*element.m_value.array)[1].m_parent = this; +#endif m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); @@ -18276,6 +18279,12 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); +#ifdef JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif } assert_invariant(); @@ -19320,6 +19329,49 @@ class basic_json /// @} private: +#ifdef JSON_DIAGNOSTICS + std::string diagnostics() + { + std::string result; + for (basic_json* current = this; 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 (current->m_parent->m_value.array->operator[](i) == *current) + { + result = "/" + std::to_string(i) + result; + continue; + } + } + break; + } + + case value_t::object: + { + for (auto it : *current->m_parent->m_value.object) + { + if (it.second == *current) + { + result = "/" + it.first + result; + continue; + } + } + break; + } + + default: + break; + } + } + + return result; + } +#endif + ////////////////// // value access // ////////////////// @@ -19942,7 +19994,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->at(idx); + result.m_parent = this; + return result; +#else return m_value.array->at(idx); +#endif } JSON_CATCH (std::out_of_range&) { @@ -20040,7 +20098,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->at(key); + result.m_parent = this; + return result; +#else return m_value.object->at(key); +#endif } JSON_CATCH (std::out_of_range&) { @@ -20149,9 +20213,18 @@ class basic_json m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->operator[](idx); + result.m_parent = this; + return result; +#else return m_value.array->operator[](idx); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); @@ -20227,7 +20300,13 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -20317,7 +20396,13 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -21873,6 +21958,9 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -21908,6 +21996,9 @@ class basic_json // add element to array m_value.array->push_back(val); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } /*! @@ -21956,8 +22047,13 @@ class basic_json assert_invariant(); } - // add element to array + // add element to object +#ifdef JSON_DIAGNOSTICS + auto res = m_value.object->insert(val); + res.first->second.m_parent = this; +#else m_value.object->insert(val); +#endif } /*! @@ -22061,9 +22157,18 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->emplace_back(std::forward(args)...); + result.m_parent = this; + return result; +#else return m_value.array->emplace_back(std::forward(args)...); +#endif #else m_value.array->emplace_back(std::forward(args)...); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif return m_value.array->back(); #endif } @@ -23591,6 +23696,11 @@ class basic_json /// the value of the current element json_value m_value = {}; +#ifdef JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + ////////////////////////////////////////// // binary serialization/deserialization // //////////////////////////////////////////