diff options
Diffstat (limited to 'json4cpp/tests/src/unit-custom-base-class.cpp')
| -rw-r--r-- | json4cpp/tests/src/unit-custom-base-class.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/json4cpp/tests/src/unit-custom-base-class.cpp b/json4cpp/tests/src/unit-custom-base-class.cpp new file mode 100644 index 0000000000..7dab5c5766 --- /dev/null +++ b/json4cpp/tests/src/unit-custom-base-class.cpp @@ -0,0 +1,335 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me> +// SPDX-License-Identifier: MIT + +#include <set> +#include <sstream> +#include <string> + +#include "doctest_compatibility.h" + +#include <nlohmann/json.hpp> + +// Test extending nlohmann::json by using a custom base class. +// Add some metadata to each node and test the behaviour of copy / move +template<class MetaDataType> +class json_metadata +{ + public: + using metadata_t = MetaDataType; + metadata_t& metadata() + { + return m_metadata; + } + const metadata_t& metadata() const + { + return m_metadata; + } + private: + metadata_t m_metadata = {}; +}; + +template<class T> +using json_with_metadata = + nlohmann::basic_json < + std::map, + std::vector, + std::string, + bool, + std::int64_t, + std::uint64_t, + double, + std::allocator, + nlohmann::adl_serializer, + std::vector<std::uint8_t>, + json_metadata<T> + >; + +TEST_CASE("JSON Node Metadata") +{ + SECTION("type int") + { + using json = json_with_metadata<int>; + json null; + auto obj = json::object(); + auto array = json::array(); + + null.metadata() = 1; + obj.metadata() = 2; + array.metadata() = 3; + auto copy = array; + + CHECK(null.metadata() == 1); + CHECK(obj.metadata() == 2); + CHECK(array.metadata() == 3); + CHECK(copy.metadata() == 3); + } + SECTION("type vector<int>") + { + using json = json_with_metadata<std::vector<int>>; + json value; + value.metadata().emplace_back(1); + auto copy = value; + value.metadata().emplace_back(2); + + CHECK(copy.metadata().size() == 1); + CHECK(copy.metadata().at(0) == 1); + CHECK(value.metadata().size() == 2); + CHECK(value.metadata().at(0) == 1); + CHECK(value.metadata().at(1) == 2); + } + SECTION("copy ctor") + { + using json = json_with_metadata<std::vector<int>>; + json value; + value.metadata().emplace_back(1); + value.metadata().emplace_back(2); + + json copy = value; + + CHECK(copy.metadata().size() == 2); + CHECK(copy.metadata().at(0) == 1); + CHECK(copy.metadata().at(1) == 2); + CHECK(value.metadata().size() == 2); + CHECK(value.metadata().at(0) == 1); + CHECK(value.metadata().at(1) == 2); + + value.metadata().clear(); + CHECK(copy.metadata().size() == 2); + CHECK(value.metadata().size() == 0); + } + SECTION("move ctor") + { + using json = json_with_metadata<std::vector<int>>; + json value; + value.metadata().emplace_back(1); + value.metadata().emplace_back(2); + + const json moved = std::move(value); + + CHECK(moved.metadata().size() == 2); + CHECK(moved.metadata().at(0) == 1); + CHECK(moved.metadata().at(1) == 2); + } + SECTION("move assign") + { + using json = json_with_metadata<std::vector<int>>; + json value; + value.metadata().emplace_back(1); + value.metadata().emplace_back(2); + + json moved; + moved = std::move(value); + + CHECK(moved.metadata().size() == 2); + CHECK(moved.metadata().at(0) == 1); + CHECK(moved.metadata().at(1) == 2); + } + SECTION("copy assign") + { + using json = json_with_metadata<std::vector<int>>; + json value; + value.metadata().emplace_back(1); + value.metadata().emplace_back(2); + + json copy; + copy = value; + + CHECK(copy.metadata().size() == 2); + CHECK(copy.metadata().at(0) == 1); + CHECK(copy.metadata().at(1) == 2); + CHECK(value.metadata().size() == 2); + CHECK(value.metadata().at(0) == 1); + CHECK(value.metadata().at(1) == 2); + + value.metadata().clear(); + CHECK(copy.metadata().size() == 2); + CHECK(value.metadata().size() == 0); + } + SECTION("type unique_ptr<int>") + { + using json = json_with_metadata<std::unique_ptr<int>>; + json value; + value.metadata().reset(new int(42)); // NOLINT(cppcoreguidelines-owning-memory) + auto moved = std::move(value); + + CHECK(moved.metadata() != nullptr); + CHECK(*moved.metadata() == 42); + } + SECTION("type vector<int> in json array") + { + using json = json_with_metadata<std::vector<int>>; + json value; + value.metadata().emplace_back(1); + value.metadata().emplace_back(2); + + json const array(10, value); + + CHECK(value.metadata().size() == 2); + CHECK(value.metadata().at(0) == 1); + CHECK(value.metadata().at(1) == 2); + + for (const auto& val : array) + { + CHECK(val.metadata().size() == 2); + CHECK(val.metadata().at(0) == 1); + CHECK(val.metadata().at(1) == 2); + } + } +} + +// Test extending nlohmann::json by using a custom base class. +// Add a custom member function template iterating over the whole json tree. +class visitor_adaptor +{ + public: + template <class Fnc> + void visit(const Fnc& fnc) const; + private: + template <class Ptr, class Fnc> + void do_visit(const Ptr& ptr, const Fnc& fnc) const; +}; + +using json_with_visitor_t = nlohmann::basic_json < + std::map, + std::vector, + std::string, + bool, + std::int64_t, + std::uint64_t, + double, + std::allocator, + nlohmann::adl_serializer, + std::vector<std::uint8_t>, + visitor_adaptor + >; + +template <class Fnc> +void visitor_adaptor::visit(const Fnc& fnc) const +{ + do_visit(json_with_visitor_t::json_pointer{}, fnc); +} + +template <class Ptr, class Fnc> +void visitor_adaptor::do_visit(const Ptr& ptr, const Fnc& fnc) const +{ + using value_t = nlohmann::detail::value_t; + const json_with_visitor_t& json = *static_cast<const json_with_visitor_t*>(this); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + switch (json.type()) + { + case value_t::object: + for (const auto& entry : json.items()) + { + entry.value().do_visit(ptr / entry.key(), fnc); + } + break; + case value_t::array: + for (std::size_t i = 0; i < json.size(); ++i) + { + json.at(i).do_visit(ptr / std::to_string(i), fnc); + } + break; + case value_t::discarded: + break; + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + default: + fnc(ptr, json); + } +} + +TEST_CASE("JSON Visit Node") +{ + json_with_visitor_t json; + json["null"]; + json["int"] = -1; + json["uint"] = 1U; + json["float"] = 1.0; + json["boolean"] = true; + json["string"] = "string"; + json["array"].push_back(0); + json["array"].push_back(1); + json["array"].push_back(json); + + std::set<std::string> expected + { + "/null - null - null", + "/int - number_integer - -1", + "/uint - number_unsigned - 1", + "/float - number_float - 1.0", + "/boolean - boolean - true", + "/string - string - \"string\"", + "/array/0 - number_integer - 0", + "/array/1 - number_integer - 1", + + "/array/2/null - null - null", + "/array/2/int - number_integer - -1", + "/array/2/uint - number_unsigned - 1", + "/array/2/float - number_float - 1.0", + "/array/2/boolean - boolean - true", + "/array/2/string - string - \"string\"", + "/array/2/array/0 - number_integer - 0", + "/array/2/array/1 - number_integer - 1" + }; + + json.visit( + [&](const json_with_visitor_t::json_pointer & p, + const json_with_visitor_t& j) + { + std::stringstream str; + str << p.to_string() << " - " ; + using value_t = nlohmann::detail::value_t; + switch (j.type()) + { + case value_t::object: + str << "object"; + break; + case value_t::array: + str << "array"; + break; + case value_t::discarded: + str << "discarded"; + break; + case value_t::null: + str << "null"; + break; + case value_t::string: + str << "string"; + break; + case value_t::boolean: + str << "boolean"; + break; + case value_t::number_integer: + str << "number_integer"; + break; + case value_t::number_unsigned: + str << "number_unsigned"; + break; + case value_t::number_float: + str << "number_float"; + break; + case value_t::binary: + str << "binary"; + break; + default: + str << "error"; + break; + } + str << " - " << j.dump(); + CHECK(json.at(p) == j); + INFO(str.str()); + CHECK(expected.count(str.str()) == 1); + expected.erase(str.str()); + } + ); + CHECK(expected.empty()); +} |
