| // __ _____ _____ _____ |
| // __| | __| | | | JSON for Modern C++ (supporting code) |
| // | | |__ | | | | | | version 3.11.3 |
| // |_____|_____|_____|_|___| https://github.com/nlohmann/json |
| // |
| // Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>. |
| // SPDX-FileCopyrightText: 2013-2023 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()); |
| } |