diff --git a/doc/examples/items.cpp b/doc/examples/items.cpp new file mode 100644 index 0000000000..b2224d603e --- /dev/null +++ b/doc/examples/items.cpp @@ -0,0 +1,23 @@ +#include +#include "json.hpp" + +using json = nlohmann::json; + +int main() +{ + // create JSON values + json j_object = {{"one", 1}, {"two", 2}}; + json j_array = {1, 2, 4, 8, 16}; + + // example for an object + for (auto& x : j_object.items()) + { + std::cout << "key: " << x.key() << ", value: " << x.value() << '\n'; + } + + // example for an array + for (auto& x : j_array.items()) + { + std::cout << "key: " << x.key() << ", value: " << x.value() << '\n'; + } +} diff --git a/doc/examples/items.link b/doc/examples/items.link new file mode 100644 index 0000000000..0b74c5b390 --- /dev/null +++ b/doc/examples/items.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/items.output b/doc/examples/items.output new file mode 100644 index 0000000000..89b09f5249 --- /dev/null +++ b/doc/examples/items.output @@ -0,0 +1,7 @@ +key: one, value: 1 +key: two, value: 2 +key: 0, value: 1 +key: 1, value: 2 +key: 2, value: 4 +key: 3, value: 8 +key: 4, value: 16 diff --git a/src/json.hpp b/src/json.hpp index 5b0b0ea5b3..a12fcbe3c7 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -11375,6 +11375,68 @@ class basic_json return iteration_proxy(ref); } + /*! + @brief helper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without `items()` function: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without `items()` function: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with `items()` function: + + @code{cpp} + for (auto it : j_object.items()) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). + + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the function is used.,items} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + */ + iteration_proxy items() + { + return iteration_proxy(*this); + } + + /*! + @copydoc items() + */ + iteration_proxy items() const + { + return iteration_proxy(*this); + } + /// @} diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp index e16ce16d37..4f5bd15d25 100644 --- a/test/src/unit-iterator_wrapper.cpp +++ b/test/src/unit-iterator_wrapper.cpp @@ -727,3 +727,700 @@ TEST_CASE("iterator_wrapper") } } } + +TEST_CASE("items()") +{ + SECTION("object") + { + SECTION("value") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + + // change the value + i.value() = json(11); + CHECK(i.value() == json(11)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + + // change the value + i.value() = json(22); + CHECK(i.value() == json(22)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + + // check if values where changed + CHECK(j == json({{"A", 11}, {"B", 22}})); + } + + SECTION("const value") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("const object") + { + SECTION("value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("array") + { + SECTION("value") + { + json j = {"A", "B"}; + int counter = 1; + + for (auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + json j = {"A", "B"}; + int counter = 1; + + for (auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + + // change the value + i.value() = "AA"; + CHECK(i.value() == "AA"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + + // change the value + i.value() = "BB"; + CHECK(i.value() == "BB"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + + // check if values where changed + CHECK(j == json({"AA", "BB"})); + } + + SECTION("const value") + { + json j = {"A", "B"}; + int counter = 1; + + for (const auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + json j = {"A", "B"}; + int counter = 1; + + for (const auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("const array") + { + SECTION("value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto& i : j.items()) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("primitive") + { + SECTION("value") + { + json j = 1; + int counter = 1; + + for (auto i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("reference") + { + json j = 1; + int counter = 1; + + for (auto& i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + + // change value + i.value() = json(2); + } + + CHECK(counter == 2); + + // check if value has changed + CHECK(j == json(2)); + } + + SECTION("const value") + { + json j = 1; + int counter = 1; + + for (const auto i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const reference") + { + json j = 1; + int counter = 1; + + for (const auto& i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + } + + SECTION("const primitive") + { + SECTION("value") + { + const json j = 1; + int counter = 1; + + for (auto i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("reference") + { + const json j = 1; + int counter = 1; + + for (auto& i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const value") + { + const json j = 1; + int counter = 1; + + for (const auto i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const reference") + { + const json j = 1; + int counter = 1; + + for (const auto& i : j.items()) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + } +}