From ec0b1798bc7e45cd3aa6456e2cc829db08d1e69b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 2 Jan 2021 21:36:11 +0100 Subject: [PATCH] :construction: implement more parent relations --- CMakeLists.txt | 4 + include/nlohmann/detail/input/json_sax.hpp | 14 ++- include/nlohmann/json.hpp | 86 +++++++++++++++++- single_include/nlohmann/json.hpp | 100 +++++++++++++++++++-- test/src/unit-iterators2.cpp | 18 ++-- test/src/unit-json_patch.cpp | 14 ++- 6 files changed, 219 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index abd3a17c7..36f1cf705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,10 @@ if (NOT JSON_ImplicitConversions) message(STATUS "Implicit conversions are disabled") endif() +if (JSON_Diagnostics) + message(STATUS "Diagnostics enabled") +endif() + ## ## TARGET ## create target and add include path diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 316f51723..61266188b 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -298,12 +298,18 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::forward(v)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return object_element; } @@ -574,7 +580,10 @@ 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)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return {true, &(ref_stack.back()->m_value.array->back())}; } @@ -592,6 +601,9 @@ class json_sax_dom_callback_parser JSON_ASSERT(object_element); *object_element = std::move(value); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return {true, object_element}; } diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 80bc871a2..2fc497bae 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1872,6 +1872,12 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); +#if JSON_DIAGNOSTICS + for (auto& entry : *m_value.array) + { + entry.m_parent = this; + } +#endif assert_invariant(); } @@ -2004,6 +2010,12 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } @@ -2011,6 +2023,12 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -2074,12 +2092,24 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } case value_t::array: { m_value = *other.m_value.array; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -2205,6 +2235,9 @@ class basic_json using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); +#if JSON_DIAGNOSTICS + m_parent = other.m_parent; +#endif assert_invariant(); return *this; @@ -2229,6 +2262,9 @@ class basic_json { assert_invariant(); m_value.destroy(m_type); +#if JSON_DIAGNOSTICS + m_parent = nullptr; +#endif } /// @} @@ -2751,7 +2787,7 @@ class basic_json return ""; } - return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{}, + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, [](const std::string & a, const std::string & b) { return a + "/" + b; @@ -3604,7 +3640,10 @@ class basic_json idx - m_value.array->size() + 1, basic_json()); #if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; + for (std::size_t i = idx + 1; i < m_value.array->size(); ++i) + { + m_value.array->operator[](i).m_parent = this; + } #endif } @@ -5609,6 +5648,11 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); + +#if JSON_DIAGNOSTICS + res.first->second.m_parent = this; +#endif + // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -5671,7 +5715,13 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, val); + result->m_parent = this; + return result; +#else return insert_iterator(pos, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -5722,7 +5772,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, cnt, val); + for (size_type i = 0; i < cnt; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, cnt, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -5784,7 +5843,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + for (std::size_t i = 0; i < std::distance(first, last); ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); +#endif } /*! @@ -5826,7 +5894,17 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + const auto size = ilist.size(); + iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); + for (std::size_t i = 0; i < size; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, ilist.begin(), ilist.end()); +#endif } /*! @@ -8387,7 +8465,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, parent.diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } // default case: insert add offset @@ -8548,7 +8626,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, diagnostics() + "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, val.diagnostics() + "unsuccessful: " + val.dump())); } break; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 16a6049de..f9bb662d1 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5590,12 +5590,18 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::forward(v)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return object_element; } @@ -5866,7 +5872,10 @@ 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)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return {true, &(ref_stack.back()->m_value.array->back())}; } @@ -5884,6 +5893,9 @@ class json_sax_dom_callback_parser JSON_ASSERT(object_element); *object_element = std::move(value); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return {true, object_element}; } @@ -18496,6 +18508,12 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); +#if JSON_DIAGNOSTICS + for (auto& entry : *m_value.array) + { + entry.m_parent = this; + } +#endif assert_invariant(); } @@ -18628,6 +18646,12 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } @@ -18635,6 +18659,12 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -18698,12 +18728,24 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } case value_t::array: { m_value = *other.m_value.array; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -18829,6 +18871,9 @@ class basic_json using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); +#if JSON_DIAGNOSTICS + m_parent = other.m_parent; +#endif assert_invariant(); return *this; @@ -18853,6 +18898,9 @@ class basic_json { assert_invariant(); m_value.destroy(m_type); +#if JSON_DIAGNOSTICS + m_parent = nullptr; +#endif } /// @} @@ -19375,7 +19423,7 @@ class basic_json return ""; } - return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{}, + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, [](const std::string & a, const std::string & b) { return a + "/" + b; @@ -20228,7 +20276,10 @@ class basic_json idx - m_value.array->size() + 1, basic_json()); #if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; + for (std::size_t i = idx + 1; i < m_value.array->size(); ++i) + { + m_value.array->operator[](i).m_parent = this; + } #endif } @@ -22233,6 +22284,11 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); + +#if JSON_DIAGNOSTICS + res.first->second.m_parent = this; +#endif + // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -22295,7 +22351,13 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, val); + result->m_parent = this; + return result; +#else return insert_iterator(pos, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -22346,7 +22408,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, cnt, val); + for (size_type i = 0; i < cnt; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, cnt, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -22408,7 +22479,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + for (std::size_t i = 0; i < std::distance(first, last); ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); +#endif } /*! @@ -22450,7 +22530,17 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + const auto size = ilist.size(); + iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); + for (std::size_t i = 0; i < size; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, ilist.begin(), ilist.end()); +#endif } /*! @@ -25011,7 +25101,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, parent.diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } // default case: insert add offset @@ -25172,7 +25262,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, diagnostics() + "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, val.diagnostics() + "unsuccessful: " + val.dump())); } break; diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index b9dcc2209..c17084c16 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -271,13 +271,16 @@ TEST_CASE("iterators 2") { CHECK_THROWS_AS(j.begin() == k.begin(), json::invalid_iterator&); CHECK_THROWS_AS(j.cbegin() == k.cbegin(), json::invalid_iterator&); - CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_AS(j.begin() < k.begin(), json::invalid_iterator&); CHECK_THROWS_AS(j.cbegin() < k.cbegin(), json::invalid_iterator&); +#if JSON_DIAGNOSTICS + // the output differs in each loop, so we cannot fix a string for the expected exception +#else + CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); +#endif } } } @@ -750,13 +753,16 @@ TEST_CASE("iterators 2") { CHECK_THROWS_AS(j.rbegin() == k.rbegin(), json::invalid_iterator&); CHECK_THROWS_AS(j.crbegin() == k.crbegin(), json::invalid_iterator&); - CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_AS(j.rbegin() < k.rbegin(), json::invalid_iterator&); CHECK_THROWS_AS(j.crbegin() < k.crbegin(), json::invalid_iterator&); +#if JSON_DIAGNOSTICS + // the output differs in each loop, so we cannot fix a string for the expected exception +#else + CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); +#endif } } } diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 2ad7aadb8..cf11b5603 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -343,7 +343,11 @@ TEST_CASE("JSON patch") // check that evaluation throws CHECK_THROWS_AS(doc.patch(patch), json::other_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); +#endif } SECTION("A.10. Adding a Nested Member Object") @@ -484,7 +488,11 @@ TEST_CASE("JSON patch") // check that evaluation throws CHECK_THROWS_AS(doc.patch(patch), json::other_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); +#endif } SECTION("A.16. Adding an Array Value") @@ -1183,7 +1191,11 @@ TEST_CASE("JSON patch") // the test will fail CHECK_THROWS_AS(doc.patch(patch), json::other_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); +#endif } } } @@ -1268,7 +1280,7 @@ TEST_CASE("JSON patch") std::ifstream f(filename); json suite = json::parse(f); - for (const auto& test : suite) + for (const auto test : suite) { INFO_WITH_TEMP(test.value("comment", ""));