| // __ _____ _____ _____ | |
| // __| | __| | | | 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 "doctest_compatibility.h" | |
| #include <nlohmann/json.hpp> | |
| #ifdef JSON_TEST_NO_GLOBAL_UDLS | |
| using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) | |
| #endif | |
| // build test with C++14 | |
| // JSON_HAS_CPP_14 | |
| // used to check at compile time (via is_detected) whether a call is well-formed; see | |
| // https://github.com/nlohmann/json/issues/5657 | |
| // | |
| // note: an integer *literal* (rather than a std::declval<T>() of integral type T) is required to | |
| // reproduce the bug, because only a null pointer constant--an integer literal with value zero, not | |
| // merely a runtime value that happens to be zero--implicitly converts to a null const char*; that is | |
| // why can_call_*_with_0 below hard-code the literal 0 instead of taking it as a template argument | |
| template<typename BasicJsonType, typename T> | |
| using can_call_find = decltype(std::declval<BasicJsonType>().find(std::declval<T>())); | |
| template<typename BasicJsonType, typename T> | |
| using can_call_count = decltype(std::declval<BasicJsonType>().count(std::declval<T>())); | |
| template<typename BasicJsonType, typename T> | |
| using can_call_contains = decltype(std::declval<BasicJsonType>().contains(std::declval<T>())); | |
| template<typename BasicJsonType, typename KeyType, typename ValueType> | |
| using can_call_value = decltype(std::declval<BasicJsonType>().value(std::declval<KeyType>(), std::declval<ValueType>())); | |
| template<typename BasicJsonType> | |
| using can_call_find_with_0 = decltype(std::declval<BasicJsonType>().find(0)); | |
| template<typename BasicJsonType> | |
| using can_call_count_with_0 = decltype(std::declval<BasicJsonType>().count(0)); | |
| template<typename BasicJsonType> | |
| using can_call_contains_with_0 = decltype(std::declval<BasicJsonType>().contains(0)); | |
| template<typename BasicJsonType> | |
| using can_call_contains_with_0L = decltype(std::declval<BasicJsonType>().contains(0L)); | |
| template<typename BasicJsonType> | |
| using can_call_value_with_0 = decltype(std::declval<BasicJsonType>().value(0, 1)); | |
| TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_json) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) | |
| { | |
| SECTION("object") | |
| { | |
| Json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", Json::object()}, {"array", {1, 2, 3}}}; | |
| const Json j_const = j; | |
| SECTION("access specified element with bounds checking") | |
| { | |
| SECTION("access within bounds") | |
| { | |
| CHECK(j.at("integer") == Json(1)); | |
| CHECK(j.at("unsigned") == Json(1u)); | |
| CHECK(j.at("boolean") == Json(true)); | |
| CHECK(j.at("null") == Json(nullptr)); | |
| CHECK(j.at("string") == Json("hello world")); | |
| CHECK(j.at("floating") == Json(42.23)); | |
| CHECK(j.at("object") == Json::object()); | |
| CHECK(j.at("array") == Json({1, 2, 3})); | |
| CHECK(j_const.at("integer") == Json(1)); | |
| CHECK(j_const.at("unsigned") == Json(1u)); | |
| CHECK(j_const.at("boolean") == Json(true)); | |
| CHECK(j_const.at("null") == Json(nullptr)); | |
| CHECK(j_const.at("string") == Json("hello world")); | |
| CHECK(j_const.at("floating") == Json(42.23)); | |
| CHECK(j_const.at("object") == Json::object()); | |
| CHECK(j_const.at("array") == Json({1, 2, 3})); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.at(std::string_view("integer")) == Json(1)); | |
| CHECK(j.at(std::string_view("unsigned")) == Json(1u)); | |
| CHECK(j.at(std::string_view("boolean")) == Json(true)); | |
| CHECK(j.at(std::string_view("null")) == Json(nullptr)); | |
| CHECK(j.at(std::string_view("string")) == Json("hello world")); | |
| CHECK(j.at(std::string_view("floating")) == Json(42.23)); | |
| CHECK(j.at(std::string_view("object")) == Json::object()); | |
| CHECK(j.at(std::string_view("array")) == Json({1, 2, 3})); | |
| CHECK(j_const.at(std::string_view("integer")) == Json(1)); | |
| CHECK(j_const.at(std::string_view("unsigned")) == Json(1u)); | |
| CHECK(j_const.at(std::string_view("boolean")) == Json(true)); | |
| CHECK(j_const.at(std::string_view("null")) == Json(nullptr)); | |
| CHECK(j_const.at(std::string_view("string")) == Json("hello world")); | |
| CHECK(j_const.at(std::string_view("floating")) == Json(42.23)); | |
| CHECK(j_const.at(std::string_view("object")) == Json::object()); | |
| CHECK(j_const.at(std::string_view("array")) == Json({1, 2, 3})); | |
| #endif | |
| } | |
| SECTION("access outside bounds") | |
| { | |
| CHECK_THROWS_WITH_AS(j.at("foo"), "[json.exception.out_of_range.403] key 'foo' not found", typename Json::out_of_range&); | |
| CHECK_THROWS_WITH_AS(j_const.at("foo"), "[json.exception.out_of_range.403] key 'foo' not found", typename Json::out_of_range&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j.at(std::string_view("foo")), "[json.exception.out_of_range.403] key 'foo' not found", typename Json::out_of_range&); | |
| CHECK_THROWS_WITH_AS(j_const.at(std::string_view("foo")), "[json.exception.out_of_range.403] key 'foo' not found", typename Json::out_of_range&); | |
| #endif | |
| } | |
| SECTION("access on non-object type") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonobject(Json::value_t::null); | |
| const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) | |
| CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with null", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view(std::string_view("foo"))), "[json.exception.type_error.304] cannot use at() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view(std::string_view("foo"))), "[json.exception.type_error.304] cannot use at() with null", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonobject(Json::value_t::boolean); | |
| const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) | |
| CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with boolean", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonobject(Json::value_t::string); | |
| const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) | |
| CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with string", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with string", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("array") | |
| { | |
| Json j_nonobject(Json::value_t::array); | |
| const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) | |
| CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with array", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with array", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonobject(Json::value_t::number_integer); | |
| const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) | |
| CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (unsigned)") | |
| { | |
| Json j_nonobject(Json::value_t::number_unsigned); | |
| const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) | |
| CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonobject(Json::value_t::number_float); | |
| const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) | |
| CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| } | |
| } | |
| SECTION("access specified element with default value") | |
| { | |
| SECTION("given a key") | |
| { | |
| SECTION("access existing value") | |
| { | |
| CHECK(j.value("integer", 2) == 1); | |
| CHECK(j.value("integer", 1.0) == Approx(1)); | |
| CHECK(j.value("unsigned", 2) == 1u); | |
| CHECK(j.value("unsigned", 1.0) == Approx(1u)); | |
| CHECK(j.value("null", Json(1)) == Json()); | |
| CHECK(j.value("boolean", false) == true); | |
| CHECK(j.value("string", "bar") == "hello world"); | |
| CHECK(j.value("string", std::string("bar")) == "hello world"); | |
| CHECK(j.value("floating", 12.34) == Approx(42.23)); | |
| CHECK(j.value("floating", 12) == 42); | |
| CHECK(j.value("object", Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j.value("array", Json({10, 100})) == Json({1, 2, 3})); | |
| CHECK(j_const.value("integer", 2) == 1); | |
| CHECK(j_const.value("integer", 1.0) == Approx(1)); | |
| CHECK(j_const.value("unsigned", 2) == 1u); | |
| CHECK(j_const.value("unsigned", 1.0) == Approx(1u)); | |
| CHECK(j_const.value("boolean", false) == true); | |
| CHECK(j_const.value("string", "bar") == "hello world"); | |
| CHECK(j_const.value("string", std::string("bar")) == "hello world"); | |
| CHECK(j_const.value("floating", 12.34) == Approx(42.23)); | |
| CHECK(j_const.value("floating", 12) == 42); | |
| CHECK(j_const.value("object", Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j_const.value("array", Json({10, 100})) == Json({1, 2, 3})); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.value(std::string_view("integer"), 2) == 1); | |
| CHECK(j.value(std::string_view("integer"), 1.0) == Approx(1)); | |
| CHECK(j.value(std::string_view("unsigned"), 2) == 1u); | |
| CHECK(j.value(std::string_view("unsigned"), 1.0) == Approx(1u)); | |
| CHECK(j.value(std::string_view("null"), Json(1)) == Json()); | |
| CHECK(j.value(std::string_view("boolean"), false) == true); | |
| CHECK(j.value(std::string_view("string"), "bar") == "hello world"); | |
| CHECK(j.value(std::string_view("string"), std::string("bar")) == "hello world"); | |
| CHECK(j.value(std::string_view("floating"), 12.34) == Approx(42.23)); | |
| CHECK(j.value(std::string_view("floating"), 12) == 42); | |
| CHECK(j.value(std::string_view("object"), Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j.value(std::string_view("array"), Json({10, 100})) == Json({1, 2, 3})); | |
| CHECK(j_const.value(std::string_view("integer"), 2) == 1); | |
| CHECK(j_const.value(std::string_view("integer"), 1.0) == Approx(1)); | |
| CHECK(j_const.value(std::string_view("unsigned"), 2) == 1u); | |
| CHECK(j_const.value(std::string_view("unsigned"), 1.0) == Approx(1u)); | |
| CHECK(j_const.value(std::string_view("boolean"), false) == true); | |
| CHECK(j_const.value(std::string_view("string"), "bar") == "hello world"); | |
| CHECK(j_const.value(std::string_view("string"), std::string("bar")) == "hello world"); | |
| CHECK(j_const.value(std::string_view("floating"), 12.34) == Approx(42.23)); | |
| CHECK(j_const.value(std::string_view("floating"), 12) == 42); | |
| CHECK(j_const.value(std::string_view("object"), Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j_const.value(std::string_view("array"), Json({10, 100})) == Json({1, 2, 3})); | |
| #endif | |
| } | |
| SECTION("access non-existing value") | |
| { | |
| CHECK(j.value("_", 2) == 2); | |
| CHECK(j.value("_", 2u) == 2u); | |
| CHECK(j.value("_", false) == false); | |
| CHECK(j.value("_", "bar") == "bar"); | |
| CHECK(j.value("_", 12.34) == Approx(12.34)); | |
| CHECK(j.value("_", Json({{"foo", "bar"}})) == Json({{"foo", "bar"}})); | |
| CHECK(j.value("_", Json({10, 100})) == Json({10, 100})); | |
| CHECK(j_const.value("_", 2) == 2); | |
| CHECK(j_const.value("_", 2u) == 2u); | |
| CHECK(j_const.value("_", false) == false); | |
| CHECK(j_const.value("_", "bar") == "bar"); | |
| CHECK(j_const.value("_", 12.34) == Approx(12.34)); | |
| CHECK(j_const.value("_", Json({{"foo", "bar"}})) == Json({{"foo", "bar"}})); | |
| CHECK(j_const.value("_", Json({10, 100})) == Json({10, 100})); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.value(std::string_view("_"), 2) == 2); | |
| CHECK(j.value(std::string_view("_"), 2u) == 2u); | |
| CHECK(j.value(std::string_view("_"), false) == false); | |
| CHECK(j.value(std::string_view("_"), "bar") == "bar"); | |
| CHECK(j.value(std::string_view("_"), 12.34) == Approx(12.34)); | |
| CHECK(j.value(std::string_view("_"), Json({{"foo", "bar"}})) == Json({{"foo", "bar"}})); | |
| CHECK(j.value(std::string_view("_"), Json({10, 100})) == Json({10, 100})); | |
| CHECK(j_const.value(std::string_view("_"), 2) == 2); | |
| CHECK(j_const.value(std::string_view("_"), 2u) == 2u); | |
| CHECK(j_const.value(std::string_view("_"), false) == false); | |
| CHECK(j_const.value(std::string_view("_"), "bar") == "bar"); | |
| CHECK(j_const.value(std::string_view("_"), 12.34) == Approx(12.34)); | |
| CHECK(j_const.value(std::string_view("_"), Json({{"foo", "bar"}})) == Json({{"foo", "bar"}})); | |
| CHECK(j_const.value(std::string_view("_"), Json({10, 100})) == Json({10, 100})); | |
| #endif | |
| } | |
| SECTION("access on non-object type") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonobject(Json::value_t::null); | |
| const Json j_nonobject_const(Json::value_t::null); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonobject(Json::value_t::boolean); | |
| const Json j_nonobject_const(Json::value_t::boolean); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with boolean", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonobject(Json::value_t::string); | |
| const Json j_nonobject_const(Json::value_t::string); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with string", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("array") | |
| { | |
| Json j_nonobject(Json::value_t::array); | |
| const Json j_nonobject_const(Json::value_t::array); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with array", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonobject(Json::value_t::number_integer); | |
| const Json j_nonobject_const(Json::value_t::number_integer); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (unsigned)") | |
| { | |
| Json j_nonobject(Json::value_t::number_unsigned); | |
| const Json j_nonobject_const(Json::value_t::number_unsigned); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonobject(Json::value_t::number_float); | |
| const Json j_nonobject_const(Json::value_t::number_float); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| } | |
| } | |
| SECTION("given a JSON pointer") | |
| { | |
| SECTION("access existing value") | |
| { | |
| CHECK(j.value("/integer"_json_pointer, 2) == 1); | |
| CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1)); | |
| CHECK(j.value("/unsigned"_json_pointer, 2) == 1u); | |
| CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); | |
| CHECK(j.value("/null"_json_pointer, Json(1)) == Json()); | |
| CHECK(j.value("/boolean"_json_pointer, false) == true); | |
| CHECK(j.value("/string"_json_pointer, "bar") == "hello world"); | |
| CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world"); | |
| CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23)); | |
| CHECK(j.value("/floating"_json_pointer, 12) == 42); | |
| CHECK(j.value("/object"_json_pointer, Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j.value("/array"_json_pointer, Json({10, 100})) == Json({1, 2, 3})); | |
| CHECK(j_const.value("/integer"_json_pointer, 2) == 1); | |
| CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1)); | |
| CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u); | |
| CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); | |
| CHECK(j_const.value("/boolean"_json_pointer, false) == true); | |
| CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world"); | |
| CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world"); | |
| CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23)); | |
| CHECK(j_const.value("/floating"_json_pointer, 12) == 42); | |
| CHECK(j_const.value("/object"_json_pointer, Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j_const.value("/array"_json_pointer, Json({10, 100})) == Json({1, 2, 3})); | |
| } | |
| SECTION("access on non-object type") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonobject(Json::value_t::null); | |
| const Json j_nonobject_const(Json::value_t::null); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonobject(Json::value_t::boolean); | |
| const Json j_nonobject_const(Json::value_t::boolean); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with boolean", typename Json::type_error&); | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonobject(Json::value_t::string); | |
| const Json j_nonobject_const(Json::value_t::string); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with string", typename Json::type_error&); | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonobject(Json::value_t::number_integer); | |
| const Json j_nonobject_const(Json::value_t::number_integer); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| } | |
| SECTION("number (unsigned)") | |
| { | |
| Json j_nonobject(Json::value_t::number_unsigned); | |
| const Json j_nonobject_const(Json::value_t::number_unsigned); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonobject(Json::value_t::number_float); | |
| const Json j_nonobject_const(Json::value_t::number_float); | |
| CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", typename Json::type_error&); | |
| } | |
| } | |
| SECTION("access on array type") | |
| { | |
| Json j_array = Json::array({j}); | |
| const Json j_array_const = Json::array({j}); | |
| CHECK(j_array.value("/0/integer"_json_pointer, 2) == 1); | |
| CHECK(j_array.value("/0/integer"_json_pointer, 1.0) == Approx(1)); | |
| CHECK(j_array.value("/0/unsigned"_json_pointer, 2) == 1u); | |
| CHECK(j_array.value("/0/unsigned"_json_pointer, 1.0) == Approx(1u)); | |
| CHECK(j_array.value("/0/null"_json_pointer, Json(1)) == Json()); | |
| CHECK(j_array.value("/0/boolean"_json_pointer, false) == true); | |
| CHECK(j_array.value("/0/string"_json_pointer, "bar") == "hello world"); | |
| CHECK(j_array.value("/0/string"_json_pointer, std::string("bar")) == "hello world"); | |
| CHECK(j_array.value("/0/floating"_json_pointer, 12.34) == Approx(42.23)); | |
| CHECK(j_array.value("/0/floating"_json_pointer, 12) == 42); | |
| CHECK(j_array.value("/0/object"_json_pointer, Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j_array.value("/0/array"_json_pointer, Json({10, 100})) == Json({1, 2, 3})); | |
| CHECK(j_array_const.value("/0/integer"_json_pointer, 2) == 1); | |
| CHECK(j_array_const.value("/0/integer"_json_pointer, 1.0) == Approx(1)); | |
| CHECK(j_array_const.value("/0/unsigned"_json_pointer, 2) == 1u); | |
| CHECK(j_array_const.value("/0/unsigned"_json_pointer, 1.0) == Approx(1u)); | |
| CHECK(j_array_const.value("/0/boolean"_json_pointer, false) == true); | |
| CHECK(j_array_const.value("/0/string"_json_pointer, "bar") == "hello world"); | |
| CHECK(j_array_const.value("/0/string"_json_pointer, std::string("bar")) == "hello world"); | |
| CHECK(j_array_const.value("/0/floating"_json_pointer, 12.34) == Approx(42.23)); | |
| CHECK(j_array_const.value("/0/floating"_json_pointer, 12) == 42); | |
| CHECK(j_array_const.value("/0/object"_json_pointer, Json({{"foo", "bar"}})) == Json::object()); | |
| CHECK(j_array_const.value("/0/array"_json_pointer, Json({10, 100})) == Json({1, 2, 3})); | |
| // Test out-of-range array index | |
| CHECK(j_array.value("/10"_json_pointer, 42) == 42); | |
| CHECK(j_array_const.value("/10"_json_pointer, 42) == 42); | |
| // Test "-" index (append position is invalid) | |
| CHECK(j_array.value("/-"_json_pointer, 42) == 42); | |
| CHECK(j_array_const.value("/-"_json_pointer, 42) == 42); | |
| // Test an index with a non-digit after a valid leading digit; this is | |
| // out_of_range (not parse_error) and must not throw (see #5672) | |
| CHECK(j_array.value("/1a"_json_pointer, 42) == 42); | |
| CHECK(j_array_const.value("/1a"_json_pointer, 42) == 42); | |
| // Test the empty reference token (JSON pointer "/"); see #5672 | |
| CHECK(j_array.value("/"_json_pointer, 42) == 42); | |
| CHECK(j_array_const.value("/"_json_pointer, 42) == 42); | |
| // Test an index whose magnitude does not fit into size_type (see #5672) | |
| CHECK(j_array.value("/99999999999999999999999"_json_pointer, 42) == 42); | |
| CHECK(j_array_const.value("/99999999999999999999999"_json_pointer, 42) == 42); | |
| CHECK(j_array.value("/18446744073709551615"_json_pointer, 42) == 42); | |
| CHECK(j_array_const.value("/18446744073709551615"_json_pointer, 42) == 42); | |
| #if !defined(JSON_NOEXCEPTION) | |
| // Test malformed index (non-numeric) throws parse_error | |
| CHECK_THROWS_WITH_AS(j_array.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&); | |
| CHECK_THROWS_WITH_AS(j_array_const.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&); | |
| CHECK_THROWS_WITH_AS(j_array.value("/x"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'x' is not a number", typename Json::parse_error&); | |
| CHECK_THROWS_WITH_AS(j_array_const.value("/x"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'x' is not a number", typename Json::parse_error&); | |
| // Test leading-zero index throws parse_error | |
| CHECK_THROWS_WITH_AS(j_array.value("/01"_json_pointer, 1), "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", typename Json::parse_error&); | |
| CHECK_THROWS_WITH_AS(j_array_const.value("/01"_json_pointer, 1), "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", typename Json::parse_error&); | |
| #endif | |
| } | |
| } | |
| } | |
| SECTION("non-const operator[]") | |
| { | |
| { | |
| Json j_null; | |
| CHECK(j_null.is_null()); | |
| j_null["key"] = 1; | |
| CHECK(j_null.is_object()); | |
| CHECK(j_null.size() == 1); | |
| j_null["key"] = 2; | |
| CHECK(j_null.size() == 1); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| { | |
| std::string_view const key = "key"; | |
| Json j_null; | |
| CHECK(j_null.is_null()); | |
| j_null[key] = 1; | |
| CHECK(j_null.is_object()); | |
| CHECK(j_null.size() == 1); | |
| j_null[key] = 2; | |
| CHECK(j_null.size() == 1); | |
| } | |
| #endif | |
| } | |
| SECTION("front and back") | |
| { | |
| if (std::is_same<Json, nlohmann::ordered_json>::value) | |
| { | |
| // "integer" is the first key | |
| CHECK(j.front() == Json(1)); | |
| CHECK(j_const.front() == Json(1)); | |
| // "array" is last key | |
| CHECK(j.back() == Json({1, 2, 3})); | |
| CHECK(j_const.back() == Json({1, 2, 3})); | |
| } | |
| else | |
| { | |
| // "array" is the smallest key | |
| CHECK(j.front() == Json({1, 2, 3})); | |
| CHECK(j_const.front() == Json({1, 2, 3})); | |
| // "unsigned" is the largest key | |
| CHECK(j.back() == Json(1u)); | |
| CHECK(j_const.back() == Json(1u)); | |
| } | |
| } | |
| SECTION("access specified element") | |
| { | |
| SECTION("access within bounds") | |
| { | |
| CHECK(j["integer"] == Json(1)); | |
| CHECK(j[typename Json::object_t::key_type("integer")] == j["integer"]); | |
| CHECK(j["unsigned"] == Json(1u)); | |
| CHECK(j[typename Json::object_t::key_type("unsigned")] == j["unsigned"]); | |
| CHECK(j["boolean"] == Json(true)); | |
| CHECK(j[typename Json::object_t::key_type("boolean")] == j["boolean"]); | |
| CHECK(j["null"] == Json(nullptr)); | |
| CHECK(j[typename Json::object_t::key_type("null")] == j["null"]); | |
| CHECK(j["string"] == Json("hello world")); | |
| CHECK(j[typename Json::object_t::key_type("string")] == j["string"]); | |
| CHECK(j["floating"] == Json(42.23)); | |
| CHECK(j[typename Json::object_t::key_type("floating")] == j["floating"]); | |
| CHECK(j["object"] == Json::object()); | |
| CHECK(j[typename Json::object_t::key_type("object")] == j["object"]); | |
| CHECK(j["array"] == Json({1, 2, 3})); | |
| CHECK(j[typename Json::object_t::key_type("array")] == j["array"]); | |
| CHECK(j_const["integer"] == Json(1)); | |
| CHECK(j_const[typename Json::object_t::key_type("integer")] == j["integer"]); | |
| CHECK(j_const["boolean"] == Json(true)); | |
| CHECK(j_const[typename Json::object_t::key_type("boolean")] == j["boolean"]); | |
| CHECK(j_const["null"] == Json(nullptr)); | |
| CHECK(j_const[typename Json::object_t::key_type("null")] == j["null"]); | |
| CHECK(j_const["string"] == Json("hello world")); | |
| CHECK(j_const[typename Json::object_t::key_type("string")] == j["string"]); | |
| CHECK(j_const["floating"] == Json(42.23)); | |
| CHECK(j_const[typename Json::object_t::key_type("floating")] == j["floating"]); | |
| CHECK(j_const["object"] == Json::object()); | |
| CHECK(j_const[typename Json::object_t::key_type("object")] == j["object"]); | |
| CHECK(j_const["array"] == Json({1, 2, 3})); | |
| CHECK(j_const[typename Json::object_t::key_type("array")] == j["array"]); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| SECTION("access within bounds (string_view)") | |
| { | |
| CHECK(j["integer"] == Json(1)); | |
| CHECK(j[std::string_view("integer")] == j["integer"]); | |
| CHECK(j["unsigned"] == Json(1u)); | |
| CHECK(j[std::string_view("unsigned")] == j["unsigned"]); | |
| CHECK(j["boolean"] == Json(true)); | |
| CHECK(j[std::string_view("boolean")] == j["boolean"]); | |
| CHECK(j["null"] == Json(nullptr)); | |
| CHECK(j[std::string_view("null")] == j["null"]); | |
| CHECK(j["string"] == Json("hello world")); | |
| CHECK(j[std::string_view("string")] == j["string"]); | |
| CHECK(j["floating"] == Json(42.23)); | |
| CHECK(j[std::string_view("floating")] == j["floating"]); | |
| CHECK(j["object"] == Json::object()); | |
| CHECK(j[std::string_view("object")] == j["object"]); | |
| CHECK(j["array"] == Json({1, 2, 3})); | |
| CHECK(j[std::string_view("array")] == j["array"]); | |
| CHECK(j_const["integer"] == Json(1)); | |
| CHECK(j_const[std::string_view("integer")] == j["integer"]); | |
| CHECK(j_const["boolean"] == Json(true)); | |
| CHECK(j_const[std::string_view("boolean")] == j["boolean"]); | |
| CHECK(j_const["null"] == Json(nullptr)); | |
| CHECK(j_const[std::string_view("null")] == j["null"]); | |
| CHECK(j_const["string"] == Json("hello world")); | |
| CHECK(j_const[std::string_view("string")] == j["string"]); | |
| CHECK(j_const["floating"] == Json(42.23)); | |
| CHECK(j_const[std::string_view("floating")] == j["floating"]); | |
| CHECK(j_const["object"] == Json::object()); | |
| CHECK(j_const[std::string_view("object")] == j["object"]); | |
| CHECK(j_const["array"] == Json({1, 2, 3})); | |
| CHECK(j_const[std::string_view("array")] == j["array"]); | |
| } | |
| #endif | |
| SECTION("access on non-object type") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonobject(Json::value_t::null); | |
| Json j_nonobject2(Json::value_t::null); | |
| const Json j_const_nonobject(j_nonobject); | |
| CHECK_NOTHROW(j_nonobject["foo"]); | |
| CHECK_NOTHROW(j_nonobject2[typename Json::object_t::key_type("foo")]); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[typename Json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_NOTHROW(j_nonobject2[std::string_view("foo")]); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonobject(Json::value_t::boolean); | |
| const Json j_const_nonobject(j_nonobject); | |
| CHECK_THROWS_WITH_AS(j_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonobject(Json::value_t::string); | |
| const Json j_const_nonobject(j_nonobject); | |
| CHECK_THROWS_WITH_AS(j_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with string", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with string", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with string", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("array") | |
| { | |
| Json j_nonobject(Json::value_t::array); | |
| const Json j_const_nonobject(j_nonobject); | |
| CHECK_THROWS_WITH_AS(j_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject[typename Json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with array", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonobject(Json::value_t::number_integer); | |
| const Json j_const_nonobject(j_nonobject); | |
| CHECK_THROWS_WITH_AS(j_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (unsigned)") | |
| { | |
| Json j_nonobject(Json::value_t::number_unsigned); | |
| const Json j_const_nonobject(j_nonobject); | |
| CHECK_THROWS_WITH_AS(j_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonobject(Json::value_t::number_float); | |
| const Json j_const_nonobject(j_nonobject); | |
| CHECK_THROWS_WITH_AS(j_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[typename Json::object_t::key_type("foo")], | |
| "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", typename Json::type_error&); | |
| #endif | |
| } | |
| } | |
| } | |
| SECTION("remove specified element") | |
| { | |
| SECTION("remove element by key") | |
| { | |
| CHECK(j.find("integer") != j.end()); | |
| CHECK(j.erase("integer") == 1); | |
| CHECK(j.find("integer") == j.end()); | |
| CHECK(j.erase("integer") == 0); | |
| CHECK(j.find("unsigned") != j.end()); | |
| CHECK(j.erase("unsigned") == 1); | |
| CHECK(j.find("unsigned") == j.end()); | |
| CHECK(j.erase("unsigned") == 0); | |
| CHECK(j.find("boolean") != j.end()); | |
| CHECK(j.erase("boolean") == 1); | |
| CHECK(j.find("boolean") == j.end()); | |
| CHECK(j.erase("boolean") == 0); | |
| CHECK(j.find("null") != j.end()); | |
| CHECK(j.erase("null") == 1); | |
| CHECK(j.find("null") == j.end()); | |
| CHECK(j.erase("null") == 0); | |
| CHECK(j.find("string") != j.end()); | |
| CHECK(j.erase("string") == 1); | |
| CHECK(j.find("string") == j.end()); | |
| CHECK(j.erase("string") == 0); | |
| CHECK(j.find("floating") != j.end()); | |
| CHECK(j.erase("floating") == 1); | |
| CHECK(j.find("floating") == j.end()); | |
| CHECK(j.erase("floating") == 0); | |
| CHECK(j.find("object") != j.end()); | |
| CHECK(j.erase("object") == 1); | |
| CHECK(j.find("object") == j.end()); | |
| CHECK(j.erase("object") == 0); | |
| CHECK(j.find("array") != j.end()); | |
| CHECK(j.erase("array") == 1); | |
| CHECK(j.find("array") == j.end()); | |
| CHECK(j.erase("array") == 0); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| SECTION("remove element by key (string_view)") | |
| { | |
| CHECK(j.find(std::string_view("integer")) != j.end()); | |
| CHECK(j.erase(std::string_view("integer")) == 1); | |
| CHECK(j.find(std::string_view("integer")) == j.end()); | |
| CHECK(j.erase(std::string_view("integer")) == 0); | |
| CHECK(j.find(std::string_view("unsigned")) != j.end()); | |
| CHECK(j.erase(std::string_view("unsigned")) == 1); | |
| CHECK(j.find(std::string_view("unsigned")) == j.end()); | |
| CHECK(j.erase(std::string_view("unsigned")) == 0); | |
| CHECK(j.find(std::string_view("boolean")) != j.end()); | |
| CHECK(j.erase(std::string_view("boolean")) == 1); | |
| CHECK(j.find(std::string_view("boolean")) == j.end()); | |
| CHECK(j.erase(std::string_view("boolean")) == 0); | |
| CHECK(j.find(std::string_view("null")) != j.end()); | |
| CHECK(j.erase(std::string_view("null")) == 1); | |
| CHECK(j.find(std::string_view("null")) == j.end()); | |
| CHECK(j.erase(std::string_view("null")) == 0); | |
| CHECK(j.find(std::string_view("string")) != j.end()); | |
| CHECK(j.erase(std::string_view("string")) == 1); | |
| CHECK(j.find(std::string_view("string")) == j.end()); | |
| CHECK(j.erase(std::string_view("string")) == 0); | |
| CHECK(j.find(std::string_view("floating")) != j.end()); | |
| CHECK(j.erase(std::string_view("floating")) == 1); | |
| CHECK(j.find(std::string_view("floating")) == j.end()); | |
| CHECK(j.erase(std::string_view("floating")) == 0); | |
| CHECK(j.find(std::string_view("object")) != j.end()); | |
| CHECK(j.erase(std::string_view("object")) == 1); | |
| CHECK(j.find(std::string_view("object")) == j.end()); | |
| CHECK(j.erase(std::string_view("object")) == 0); | |
| CHECK(j.find(std::string_view("array")) != j.end()); | |
| CHECK(j.erase(std::string_view("array")) == 1); | |
| CHECK(j.find(std::string_view("array")) == j.end()); | |
| CHECK(j.erase(std::string_view("array")) == 0); | |
| } | |
| #endif | |
| SECTION("remove element by iterator") | |
| { | |
| SECTION("erase(begin())") | |
| { | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| typename Json::iterator const it2 = jobject.erase(jobject.begin()); | |
| CHECK(jobject == Json({{"b", 1}, {"c", 17u}})); | |
| CHECK(*it2 == Json(1)); | |
| } | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| typename Json::const_iterator const it2 = jobject.erase(jobject.cbegin()); | |
| CHECK(jobject == Json({{"b", 1}, {"c", 17u}})); | |
| CHECK(*it2 == Json(1)); | |
| } | |
| } | |
| SECTION("erase(begin(), end())") | |
| { | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| const typename Json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); | |
| CHECK(jobject == Json::object()); | |
| CHECK(it2 == jobject.end()); | |
| } | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| const typename Json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); | |
| CHECK(jobject == Json::object()); | |
| CHECK(it2 == jobject.cend()); | |
| } | |
| } | |
| SECTION("erase(begin(), begin())") | |
| { | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| typename Json::iterator const it2 = jobject.erase(jobject.begin(), jobject.begin()); | |
| CHECK(jobject == Json({{"a", "a"}, {"b", 1}, {"c", 17u}})); | |
| CHECK(*it2 == Json("a")); | |
| } | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| typename Json::const_iterator const it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); | |
| CHECK(jobject == Json({{"a", "a"}, {"b", 1}, {"c", 17u}})); | |
| CHECK(*it2 == Json("a")); | |
| } | |
| } | |
| SECTION("erase at offset") | |
| { | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| typename Json::iterator const it = jobject.find("b"); | |
| typename Json::iterator const it2 = jobject.erase(it); | |
| CHECK(jobject == Json({{"a", "a"}, {"c", 17u}})); | |
| CHECK(*it2 == Json(17)); | |
| } | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| typename Json::const_iterator const it = jobject.find("b"); | |
| typename Json::const_iterator const it2 = jobject.erase(it); | |
| CHECK(jobject == Json({{"a", "a"}, {"c", 17u}})); | |
| CHECK(*it2 == Json(17)); | |
| } | |
| } | |
| SECTION("erase subrange") | |
| { | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; | |
| typename Json::iterator const it2 = jobject.erase(jobject.find("b"), jobject.find("e")); | |
| CHECK(jobject == Json({{"a", "a"}, {"e", true}})); | |
| CHECK(*it2 == Json(true)); | |
| } | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; | |
| typename Json::const_iterator const it2 = jobject.erase(jobject.find("b"), jobject.find("e")); | |
| CHECK(jobject == Json({{"a", "a"}, {"e", true}})); | |
| CHECK(*it2 == Json(true)); | |
| } | |
| } | |
| SECTION("different objects") | |
| { | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; | |
| Json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject2.begin()), | |
| "[json.exception.invalid_iterator.202] iterator does not fit current value", typename Json::invalid_iterator&); | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject.begin(), jobject2.end()), | |
| "[json.exception.invalid_iterator.203] iterators do not fit current value", typename Json::invalid_iterator&); | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject2.begin(), jobject.end()), | |
| "[json.exception.invalid_iterator.203] iterators do not fit current value", typename Json::invalid_iterator&); | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject2.begin(), jobject2.end()), | |
| "[json.exception.invalid_iterator.203] iterators do not fit current value", typename Json::invalid_iterator&); | |
| } | |
| { | |
| Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; | |
| Json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject2.cbegin()), | |
| "[json.exception.invalid_iterator.202] iterator does not fit current value", typename Json::invalid_iterator&); | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), | |
| "[json.exception.invalid_iterator.203] iterators do not fit current value", typename Json::invalid_iterator&); | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), | |
| "[json.exception.invalid_iterator.203] iterators do not fit current value", typename Json::invalid_iterator&); | |
| CHECK_THROWS_WITH_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), | |
| "[json.exception.invalid_iterator.203] iterators do not fit current value", typename Json::invalid_iterator&); | |
| } | |
| } | |
| } | |
| SECTION("remove element by key in non-object type") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonobject(Json::value_t::null); | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with null", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with null", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonobject(Json::value_t::boolean); | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with boolean", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with boolean", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonobject(Json::value_t::string); | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with string", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with string", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("array") | |
| { | |
| Json j_nonobject(Json::value_t::array); | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with array", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with array", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonobject(Json::value_t::number_integer); | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonobject(Json::value_t::number_float); | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number", typename Json::type_error&); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with number", typename Json::type_error&); | |
| #endif | |
| } | |
| } | |
| } | |
| SECTION("find an element in an object") | |
| { | |
| SECTION("existing element") | |
| { | |
| for (const auto* key : | |
| {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" | |
| }) | |
| { | |
| CHECK(j.find(key) != j.end()); | |
| CHECK(*j.find(key) == j.at(key)); | |
| CHECK(j_const.find(key) != j_const.end()); | |
| CHECK(*j_const.find(key) == j_const.at(key)); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| for (const std::string_view key : | |
| {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" | |
| }) | |
| { | |
| CHECK(j.find(key) != j.end()); | |
| CHECK(*j.find(key) == j.at(key)); | |
| CHECK(j_const.find(key) != j_const.end()); | |
| CHECK(*j_const.find(key) == j_const.at(key)); | |
| } | |
| #endif | |
| } | |
| SECTION("nonexisting element") | |
| { | |
| CHECK(j.find("foo") == j.end()); | |
| CHECK(j_const.find("foo") == j_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.find(std::string_view("foo")) == j.end()); | |
| CHECK(j_const.find(std::string_view("foo")) == j_const.end()); | |
| #endif | |
| } | |
| SECTION("all types") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonarray(Json::value_t::null); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonarray(Json::value_t::string); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| SECTION("object") | |
| { | |
| Json j_nonarray(Json::value_t::object); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| SECTION("array") | |
| { | |
| Json j_nonarray(Json::value_t::array); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonarray(Json::value_t::boolean); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonarray(Json::value_t::number_integer); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| SECTION("number (unsigned)") | |
| { | |
| Json j_nonarray(Json::value_t::number_unsigned); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonarray(Json::value_t::number_float); | |
| const Json j_nonarray_const(j_nonarray); | |
| CHECK(j_nonarray.find("foo") == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); | |
| CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); | |
| #endif | |
| } | |
| } | |
| } | |
| SECTION("count keys in an object") | |
| { | |
| SECTION("existing element") | |
| { | |
| for (const auto* key : | |
| {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" | |
| }) | |
| { | |
| CHECK(j.count(key) == 1); | |
| CHECK(j_const.count(key) == 1); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| for (const std::string_view key : | |
| {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" | |
| }) | |
| { | |
| CHECK(j.count(key) == 1); | |
| CHECK(j_const.count(key) == 1); | |
| } | |
| #endif | |
| } | |
| SECTION("nonexisting element") | |
| { | |
| CHECK(j.count("foo") == 0); | |
| CHECK(j_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("all types") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonobject(Json::value_t::null); | |
| const Json j_nonobject_const(Json::value_t::null); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonobject(Json::value_t::string); | |
| const Json j_nonobject_const(Json::value_t::string); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("object") | |
| { | |
| Json j_nonobject(Json::value_t::object); | |
| const Json j_nonobject_const(Json::value_t::object); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("array") | |
| { | |
| Json j_nonobject(Json::value_t::array); | |
| const Json j_nonobject_const(Json::value_t::array); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonobject(Json::value_t::boolean); | |
| const Json j_nonobject_const(Json::value_t::boolean); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonobject(Json::value_t::number_integer); | |
| const Json j_nonobject_const(Json::value_t::number_integer); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("number (unsigned)") | |
| { | |
| Json j_nonobject(Json::value_t::number_unsigned); | |
| const Json j_nonobject_const(Json::value_t::number_unsigned); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonobject(Json::value_t::number_float); | |
| const Json j_nonobject_const(Json::value_t::number_float); | |
| CHECK(j_nonobject.count("foo") == 0); | |
| CHECK(j_nonobject_const.count("foo") == 0); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.count(std::string_view("foo")) == 0); | |
| CHECK(j_const.count(std::string_view("foo")) == 0); | |
| #endif | |
| } | |
| } | |
| } | |
| SECTION("check existence of key in an object") | |
| { | |
| SECTION("existing element") | |
| { | |
| for (const auto* key : | |
| {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" | |
| }) | |
| { | |
| CHECK(j.contains(key) == true); | |
| CHECK(j_const.contains(key) == true); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| for (const std::string_view key : | |
| {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" | |
| }) | |
| { | |
| CHECK(j.contains(key) == true); | |
| CHECK(j_const.contains(key) == true); | |
| } | |
| #endif | |
| } | |
| SECTION("nonexisting element") | |
| { | |
| CHECK(j.contains("foo") == false); | |
| CHECK(j_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j.contains(std::string_view("foo")) == false); | |
| CHECK(j_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("all types") | |
| { | |
| SECTION("null") | |
| { | |
| Json j_nonobject(Json::value_t::null); | |
| const Json j_nonobject_const(Json::value_t::null); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("string") | |
| { | |
| Json j_nonobject(Json::value_t::string); | |
| const Json j_nonobject_const(Json::value_t::string); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("object") | |
| { | |
| Json j_nonobject(Json::value_t::object); | |
| const Json j_nonobject_const(Json::value_t::object); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("array") | |
| { | |
| Json j_nonobject(Json::value_t::array); | |
| const Json j_nonobject_const(Json::value_t::array); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("boolean") | |
| { | |
| Json j_nonobject(Json::value_t::boolean); | |
| const Json j_nonobject_const(Json::value_t::boolean); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("number (integer)") | |
| { | |
| Json j_nonobject(Json::value_t::number_integer); | |
| const Json j_nonobject_const(Json::value_t::number_integer); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("number (unsigned)") | |
| { | |
| Json j_nonobject(Json::value_t::number_unsigned); | |
| const Json j_nonobject_const(Json::value_t::number_unsigned); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| SECTION("number (floating-point)") | |
| { | |
| Json j_nonobject(Json::value_t::number_float); | |
| const Json j_nonobject_const(Json::value_t::number_float); | |
| CHECK(j_nonobject.contains("foo") == false); | |
| CHECK(j_nonobject_const.contains("foo") == false); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(j_nonobject.contains(std::string_view("foo")) == false); | |
| CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); | |
| #endif | |
| } | |
| } | |
| } | |
| } | |
| SECTION("integral keys for object lookup are rejected at compile time") | |
| { | |
| // https://github.com/nlohmann/json/issues/5657: an integer literal like 0 is a null pointer | |
| // constant, which used to convert to a null const char* and from there--via undefined | |
| // behavior in the std::string constructor--to key_type, so contains(0), find(0), and | |
| // count(0) used to compile and then crash instead of failing to compile | |
| using nlohmann::detail::is_detected; | |
| // MSVC 2015 does not treat selecting a deleted function in decltype as | |
| // a substitution failure, so it detects the deleted overloads as | |
| // callable; calling them still fails to compile | |
| #if !(defined(_MSC_VER) && _MSC_VER < 1910) | |
| CHECK_FALSE(is_detected<can_call_find_with_0, Json&>::value); | |
| CHECK_FALSE(is_detected<can_call_find_with_0, const Json&>::value); | |
| CHECK_FALSE(is_detected<can_call_count_with_0, Json&>::value); | |
| CHECK_FALSE(is_detected<can_call_contains_with_0, Json&>::value); | |
| // value(0, ...) is only affected in C++11, where the comparator is not transparent; with a | |
| // transparent comparator (C++14 and later), int is already rejected for lacking a | |
| // comparison with the key type, independently of this fix | |
| CHECK_FALSE(is_detected<can_call_value_with_0, const Json&>::value); | |
| // another integral literal type must be rejected as well, not just int | |
| CHECK_FALSE(is_detected<can_call_contains_with_0L, Json&>::value); | |
| #endif | |
| // the valid overloads must remain callable | |
| CHECK(is_detected<can_call_find, Json&, const char*>::value); | |
| CHECK(is_detected<can_call_find, Json&, std::string>::value); | |
| CHECK(is_detected<can_call_count, Json&, const char*>::value); | |
| CHECK(is_detected<can_call_contains, Json&, const char*>::value); | |
| CHECK(is_detected<can_call_contains, Json&, typename Json::json_pointer>::value); | |
| CHECK(is_detected<can_call_value, const Json&, const char*, int>::value); | |
| CHECK(is_detected<can_call_value, const Json&, typename Json::json_pointer, int>::value); | |
| #ifdef JSON_HAS_CPP_17 | |
| CHECK(is_detected<can_call_find, Json&, std::string_view>::value); | |
| CHECK(is_detected<can_call_count, Json&, std::string_view>::value); | |
| CHECK(is_detected<can_call_contains, Json&, std::string_view>::value); | |
| #endif | |
| // the neighboring size_type overloads for array access are unaffected by the new | |
| // integral-key overloads above (at(), operator[](), and erase() take a size_type) | |
| Json arr = {10, 20, 30}; | |
| const Json arr_const = arr; | |
| CHECK(arr.at(0) == 10); | |
| CHECK(arr_const.at(0) == 10); | |
| CHECK(arr[0] == 10); | |
| CHECK(arr_const[0] == 10); | |
| arr.erase(0); | |
| CHECK(arr.size() == 2); | |
| } | |
| } | |
| #if !defined(JSON_NOEXCEPTION) | |
| TEST_CASE_TEMPLATE("element access 2 (throwing tests)", Json, nlohmann::json, nlohmann::ordered_json) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) | |
| { | |
| SECTION("object") | |
| { | |
| Json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", Json::object()}, {"array", {1, 2, 3}}}; | |
| const Json j_const = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", Json::object()}, {"array", {1, 2, 3}}}; | |
| SECTION("access specified element with default value") | |
| { | |
| SECTION("given a JSON pointer") | |
| { | |
| SECTION("access non-existing value") | |
| { | |
| CHECK(j.value("/not/existing"_json_pointer, 2) == 2); | |
| CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u); | |
| CHECK(j.value("/not/existing"_json_pointer, false) == false); | |
| CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar"); | |
| CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); | |
| CHECK(j.value("/not/existing"_json_pointer, Json({{"foo", "bar"}})) == Json({{"foo", "bar"}})); | |
| CHECK(j.value("/not/existing"_json_pointer, Json({10, 100})) == Json({10, 100})); | |
| // an array index that is out of range, too large to be | |
| // represented, or "-", and a token below a scalar | |
| CHECK(j.value("/array/3"_json_pointer, 2) == 2); | |
| CHECK(j.value("/array/-"_json_pointer, 2) == 2); | |
| CHECK(j.value("/array/99999999999999999999999999"_json_pointer, 2) == 2); | |
| CHECK(j.value("/integer/0"_json_pointer, 2) == 2); | |
| CHECK(j.value("/string/x"_json_pointer, 2) == 2); | |
| CHECK(j.value("/null/x"_json_pointer, 2) == 2); | |
| CHECK(j.value("/array/0"_json_pointer, 2) == 1); | |
| CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2); | |
| CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u); | |
| CHECK(j_const.value("/not/existing"_json_pointer, false) == false); | |
| CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar"); | |
| CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); | |
| CHECK(j_const.value("/not/existing"_json_pointer, Json({{"foo", "bar"}})) == Json({{"foo", "bar"}})); | |
| CHECK(j_const.value("/not/existing"_json_pointer, Json({10, 100})) == Json({10, 100})); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| // TODO(falbrechtskirchinger) merge with the other test case; clean up | |
| TEST_CASE_TEMPLATE("element access 2 (additional value() tests)", Json, nlohmann::json, nlohmann::ordered_json) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) | |
| { | |
| using string_t = typename Json::string_t; | |
| using number_integer_t = typename Json::number_integer_t; | |
| // test assumes string_t and object_t::key_type are the same | |
| REQUIRE(std::is_same<string_t, typename Json::object_t::key_type>::value); | |
| Json j | |
| { | |
| {"foo", "bar"}, | |
| {"baz", 42} | |
| }; | |
| const char* cpstr = "default"; | |
| const char castr[] = "default"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) | |
| string_t const str = "default"; | |
| number_integer_t integer = 69; | |
| std::size_t size = 69; | |
| SECTION("deduced ValueType") | |
| { | |
| SECTION("literal key") | |
| { | |
| CHECK(j.value("foo", "default") == "bar"); | |
| CHECK(j.value("foo", cpstr) == "bar"); | |
| CHECK(j.value("foo", castr) == "bar"); | |
| CHECK(j.value("foo", str) == "bar"); | |
| // this test is in fact different from the one below, | |
| // because of 0 considering const char * overloads | |
| // whereas any other number does not | |
| CHECK(j.value("baz", 0) == 42); | |
| CHECK(j.value("baz", 47) == 42); | |
| CHECK(j.value("baz", integer) == 42); | |
| CHECK(j.value("baz", size) == 42); | |
| CHECK(j.value("bar", "default") == "default"); | |
| CHECK(j.value("bar", 0) == 0); | |
| CHECK(j.value("bar", 47) == 47); | |
| CHECK(j.value("bar", integer) == integer); | |
| CHECK(j.value("bar", size) == size); | |
| CHECK_THROWS_WITH_AS(Json().value("foo", "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().value("foo", str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| SECTION("const char * key") | |
| { | |
| const char* key = "foo"; | |
| const char* key2 = "baz"; | |
| const char* key_notfound = "bar"; | |
| CHECK(j.value(key, "default") == "bar"); | |
| CHECK(j.value(key, cpstr) == "bar"); | |
| CHECK(j.value(key, castr) == "bar"); | |
| CHECK(j.value(key, str) == "bar"); | |
| CHECK(j.value(key2, 0) == 42); | |
| CHECK(j.value(key2, 47) == 42); | |
| CHECK(j.value(key2, integer) == 42); | |
| CHECK(j.value(key2, size) == 42); | |
| CHECK(j.value(key_notfound, "default") == "default"); | |
| CHECK(j.value(key_notfound, 0) == 0); | |
| CHECK(j.value(key_notfound, 47) == 47); | |
| CHECK(j.value(key_notfound, integer) == integer); | |
| CHECK(j.value(key_notfound, size) == size); | |
| CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| SECTION("const char(&)[] key") | |
| { | |
| const char key[] = "foo"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) | |
| const char key2[] = "baz"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) | |
| const char key_notfound[] = "bar"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) | |
| CHECK(j.value(key, "default") == "bar"); | |
| CHECK(j.value(key, cpstr) == "bar"); | |
| CHECK(j.value(key, castr) == "bar"); | |
| CHECK(j.value(key, str) == "bar"); | |
| CHECK(j.value(key2, 0) == 42); | |
| CHECK(j.value(key2, 47) == 42); | |
| CHECK(j.value(key2, integer) == 42); | |
| CHECK(j.value(key2, size) == 42); | |
| CHECK(j.value(key_notfound, "default") == "default"); | |
| CHECK(j.value(key_notfound, 0) == 0); | |
| CHECK(j.value(key_notfound, 47) == 47); | |
| CHECK(j.value(key_notfound, integer) == integer); | |
| CHECK(j.value(key_notfound, size) == size); | |
| CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| SECTION("string_t/object_t::key_type key") | |
| { | |
| string_t const key = "foo"; | |
| string_t const key2 = "baz"; | |
| string_t const key_notfound = "bar"; | |
| CHECK(j.value(key, "default") == "bar"); | |
| CHECK(j.value(key, cpstr) == "bar"); | |
| CHECK(j.value(key, castr) == "bar"); | |
| CHECK(j.value(key, str) == "bar"); | |
| CHECK(j.value(key2, 0) == 42); | |
| CHECK(j.value(key2, 47) == 42); | |
| CHECK(j.value(key2, integer) == 42); | |
| CHECK(j.value(key2, size) == 42); | |
| CHECK(j.value(key_notfound, "default") == "default"); | |
| CHECK(j.value(key_notfound, 0) == 0); | |
| CHECK(j.value(key_notfound, 47) == 47); | |
| CHECK(j.value(key_notfound, integer) == integer); | |
| CHECK(j.value(key_notfound, size) == size); | |
| CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| SECTION("std::string_view key") | |
| { | |
| std::string_view const key = "foo"; | |
| std::string_view const key2 = "baz"; | |
| std::string_view const key_notfound = "bar"; | |
| CHECK(j.value(key, "default") == "bar"); | |
| CHECK(j.value(key, cpstr) == "bar"); | |
| CHECK(j.value(key, castr) == "bar"); | |
| CHECK(j.value(key, str) == "bar"); | |
| CHECK(j.value(key2, 0) == 42); | |
| CHECK(j.value(key2, 47) == 42); | |
| CHECK(j.value(key2, integer) == 42); | |
| CHECK(j.value(key2, size) == 42); | |
| CHECK(j.value(key_notfound, "default") == "default"); | |
| CHECK(j.value(key_notfound, 0) == 0); | |
| CHECK(j.value(key_notfound, 47) == 47); | |
| CHECK(j.value(key_notfound, integer) == integer); | |
| CHECK(j.value(key_notfound, size) == size); | |
| CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| #endif | |
| } | |
| SECTION("explicit ValueType") | |
| { | |
| SECTION("literal key") | |
| { | |
| CHECK(j.template value<string_t>("foo", "default") == "bar"); | |
| CHECK(j.template value<string_t>("foo", cpstr) == "bar"); | |
| CHECK(j.template value<string_t>("foo", castr) == "bar"); | |
| CHECK(j.template value<string_t>("foo", str) == "bar"); | |
| CHECK(j.template value<number_integer_t>("baz", 0) == 42); | |
| CHECK(j.template value<number_integer_t>("baz", 47) == 42); | |
| CHECK(j.template value<number_integer_t>("baz", integer) == 42); | |
| CHECK(j.template value<std::size_t>("baz", 0) == 42); | |
| CHECK(j.template value<std::size_t>("baz", 47) == 42); | |
| CHECK(j.template value<std::size_t>("baz", size) == 42); | |
| CHECK(j.template value<string_t>("bar", "default") == "default"); | |
| CHECK(j.template value<number_integer_t>("bar", 0) == 0); | |
| CHECK(j.template value<number_integer_t>("bar", 47) == 47); | |
| CHECK(j.template value<number_integer_t>("bar", integer) == integer); | |
| CHECK(j.template value<std::size_t>("bar", 0) == 0); | |
| CHECK(j.template value<std::size_t>("bar", 47) == 47); | |
| CHECK(j.template value<std::size_t>("bar", size) == size); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>("foo", "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>("foo", str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| SECTION("const char * key") | |
| { | |
| const char* key = "foo"; | |
| const char* key2 = "baz"; | |
| const char* key_notfound = "bar"; | |
| CHECK(j.template value<string_t>(key, "default") == "bar"); | |
| CHECK(j.template value<string_t>(key, cpstr) == "bar"); | |
| CHECK(j.template value<string_t>(key, castr) == "bar"); | |
| CHECK(j.template value<string_t>(key, str) == "bar"); | |
| CHECK(j.template value<number_integer_t>(key2, 0) == 42); | |
| CHECK(j.template value<number_integer_t>(key2, 47) == 42); | |
| CHECK(j.template value<number_integer_t>(key2, integer) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 0) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 47) == 42); | |
| CHECK(j.template value<std::size_t>(key2, size) == 42); | |
| CHECK(j.template value<string_t>(key_notfound, "default") == "default"); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47); | |
| CHECK(j.template value<number_integer_t>(key_notfound, integer) == integer); | |
| CHECK(j.template value<std::size_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<std::size_t>(key_notfound, 47) == 47); | |
| CHECK(j.template value<std::size_t>(key_notfound, size) == size); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| SECTION("const char(&)[] key") | |
| { | |
| const char key[] = "foo"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) | |
| const char key2[] = "baz"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) | |
| const char key_notfound[] = "bar"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) | |
| CHECK(j.template value<string_t>(key, "default") == "bar"); | |
| CHECK(j.template value<string_t>(key, cpstr) == "bar"); | |
| CHECK(j.template value<string_t>(key, castr) == "bar"); | |
| CHECK(j.template value<string_t>(key, str) == "bar"); | |
| CHECK(j.template value<number_integer_t>(key2, 0) == 42); | |
| CHECK(j.template value<number_integer_t>(key2, 47) == 42); | |
| CHECK(j.template value<number_integer_t>(key2, integer) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 0) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 47) == 42); | |
| CHECK(j.template value<std::size_t>(key2, size) == 42); | |
| CHECK(j.template value<string_t>(key_notfound, "default") == "default"); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47); | |
| CHECK(j.template value<number_integer_t>(key_notfound, integer) == integer); | |
| CHECK(j.template value<std::size_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<std::size_t>(key_notfound, 47) == 47); | |
| CHECK(j.template value<std::size_t>(key_notfound, size) == size); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| SECTION("string_t/object_t::key_type key") | |
| { | |
| string_t const key = "foo"; | |
| string_t const key2 = "baz"; | |
| string_t const key_notfound = "bar"; | |
| CHECK(j.template value<string_t>(key, "default") == "bar"); | |
| CHECK(j.template value<string_t>(key, cpstr) == "bar"); | |
| CHECK(j.template value<string_t>(key, castr) == "bar"); | |
| CHECK(j.template value<string_t>(key, str) == "bar"); | |
| CHECK(j.template value<number_integer_t>(key2, 0) == 42); | |
| CHECK(j.template value<number_integer_t>(key2, 47) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 0) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 47) == 42); | |
| CHECK(j.template value<string_t>(key_notfound, "default") == "default"); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47); | |
| CHECK(j.template value<std::size_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<std::size_t>(key_notfound, 47) == 47); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| SECTION("std::string_view key") | |
| { | |
| std::string_view const key = "foo"; | |
| std::string_view const key2 = "baz"; | |
| std::string_view const key_notfound = "bar"; | |
| CHECK(j.template value<string_t>(key, "default") == "bar"); | |
| CHECK(j.template value<string_t>(key, cpstr) == "bar"); | |
| CHECK(j.template value<string_t>(key, castr) == "bar"); | |
| CHECK(j.template value<string_t>(key, str) == "bar"); | |
| CHECK(j.template value<number_integer_t>(key2, 0) == 42); | |
| CHECK(j.template value<number_integer_t>(key2, 47) == 42); | |
| CHECK(j.template value<number_integer_t>(key2, integer) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 0) == 42); | |
| CHECK(j.template value<std::size_t>(key2, 47) == 42); | |
| CHECK(j.template value<std::size_t>(key2, size) == 42); | |
| CHECK(j.template value<string_t>(key_notfound, "default") == "default"); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47); | |
| CHECK(j.template value<number_integer_t>(key_notfound, integer) == integer); | |
| CHECK(j.template value<std::size_t>(key_notfound, 0) == 0); | |
| CHECK(j.template value<std::size_t>(key_notfound, 47) == 47); | |
| CHECK(j.template value<std::size_t>(key_notfound, size) == size); | |
| CHECK(j.template value<std::string_view>(key, "default") == "bar"); | |
| CHECK(j.template value<std::string_view>(key, cpstr) == "bar"); | |
| CHECK(j.template value<std::string_view>(key, castr) == "bar"); | |
| CHECK(j.template value<std::string_view>(key, str) == "bar"); | |
| CHECK(j.template value<std::string_view>(key_notfound, "default") == "default"); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); | |
| } | |
| #endif | |
| } | |
| } | |
| #ifdef JSON_HAS_CPP_17 | |
| TEST_CASE("operator[] with user-defined std::string_view-convertible types") | |
| { | |
| using nlohmann::json; | |
| class TestClass | |
| { | |
| std::string key_data_ = "foo"; | |
| public: | |
| operator std::string_view() const | |
| { | |
| return key_data_; | |
| } | |
| }; | |
| struct TestStruct | |
| { | |
| operator std::string_view() const | |
| { | |
| return "bar"; | |
| } | |
| }; | |
| json j = {{"foo", "from_class"}, {"bar", "from_struct"}}; | |
| const TestClass foo_obj; | |
| const TestStruct bar_obj; | |
| SECTION("read access") | |
| { | |
| CHECK(j[foo_obj] == "from_class"); | |
| CHECK(j[TestClass{}] == "from_class"); | |
| CHECK(j[bar_obj] == "from_struct"); | |
| CHECK(j[TestStruct{}] == "from_struct"); | |
| } | |
| SECTION("write access") | |
| { | |
| j[TestClass{}] = "updated_class"; | |
| j[TestStruct{}] = "updated_struct"; | |
| CHECK(j["foo"] == "updated_class"); | |
| CHECK(j["bar"] == "updated_struct"); | |
| SECTION("direct std::string_view access") | |
| { | |
| CHECK(j[std::string_view{"foo"}] == "updated_class"); | |
| CHECK(j[std::string_view{"bar"}] == "updated_struct"); | |
| } | |
| } | |
| } | |
| TEST_CASE("keys convertible to std::string_view work with all lookup functions (regression test for #5663)") | |
| { | |
| // a key type convertible only to std::string_view: the case #4958 added | |
| // support for, but only the non-const operator[] compiled with it | |
| struct ViewKey | |
| { | |
| operator std::string_view() const | |
| { | |
| return "a"; | |
| } | |
| }; | |
| // a key type convertible to both std::string and std::string_view: with | |
| // 3.12.0, such a key worked with at, the const operator[], find, count and | |
| // contains via the conversion to std::string; #4958 made the KeyType&& | |
| // templates win overload resolution for it instead, and those then failed | |
| // the lookups pick the conversion to std::string_view, which leaves the one | |
| // to std::string unused; it has to exist to reproduce the ambiguity | |
| DOCTEST_CLANG_SUPPRESS_WARNING_PUSH | |
| DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") | |
| struct DualKey | |
| { | |
| operator std::string() const | |
| { | |
| return "a"; | |
| } | |
| operator std::string_view() const | |
| { | |
| return "a"; | |
| } | |
| }; | |
| DOCTEST_CLANG_SUPPRESS_WARNING_POP | |
| SECTION("nlohmann::json") | |
| { | |
| using nlohmann::json; | |
| SECTION("ViewKey") | |
| { | |
| json j = {{"a", 1}}; | |
| const json& cj = j; | |
| CHECK(j[ViewKey{}] == 1); | |
| CHECK(cj[ViewKey{}] == 1); | |
| CHECK(j.at(ViewKey{}) == 1); | |
| CHECK(cj.at(ViewKey{}) == 1); | |
| CHECK(j.find(ViewKey{}) != j.end()); | |
| CHECK(cj.find(ViewKey{}) != cj.end()); | |
| CHECK(j.count(ViewKey{}) == 1); | |
| CHECK(j.contains(ViewKey{})); | |
| CHECK(j.value(ViewKey{}, 0) == 1); | |
| CHECK(j.erase(ViewKey{}) == 1); | |
| CHECK(!j.contains("a")); | |
| } | |
| SECTION("DualKey") | |
| { | |
| json j = {{"a", 1}}; | |
| const json& cj = j; | |
| CHECK(j[DualKey{}] == 1); | |
| CHECK(cj[DualKey{}] == 1); | |
| CHECK(j.at(DualKey{}) == 1); | |
| CHECK(cj.at(DualKey{}) == 1); | |
| CHECK(j.find(DualKey{}) != j.end()); | |
| CHECK(cj.find(DualKey{}) != cj.end()); | |
| CHECK(j.count(DualKey{}) == 1); | |
| CHECK(j.contains(DualKey{})); | |
| CHECK(j.value(DualKey{}, 0) == 1); | |
| CHECK(j.erase(DualKey{}) == 1); | |
| CHECK(!j.contains("a")); | |
| } | |
| } | |
| SECTION("nlohmann::ordered_json") | |
| { | |
| using nlohmann::ordered_json; | |
| SECTION("ViewKey") | |
| { | |
| ordered_json j = {{"a", 1}}; | |
| const ordered_json& cj = j; | |
| CHECK(j[ViewKey{}] == 1); | |
| CHECK(cj[ViewKey{}] == 1); | |
| CHECK(j.at(ViewKey{}) == 1); | |
| CHECK(cj.at(ViewKey{}) == 1); | |
| CHECK(j.find(ViewKey{}) != j.end()); | |
| CHECK(cj.find(ViewKey{}) != cj.end()); | |
| CHECK(j.count(ViewKey{}) == 1); | |
| CHECK(j.contains(ViewKey{})); | |
| CHECK(j.value(ViewKey{}, 0) == 1); | |
| CHECK(j.erase(ViewKey{}) == 1); | |
| CHECK(!j.contains("a")); | |
| } | |
| SECTION("DualKey") | |
| { | |
| ordered_json j = {{"a", 1}}; | |
| const ordered_json& cj = j; | |
| CHECK(j[DualKey{}] == 1); | |
| CHECK(cj[DualKey{}] == 1); | |
| CHECK(j.at(DualKey{}) == 1); | |
| CHECK(cj.at(DualKey{}) == 1); | |
| CHECK(j.find(DualKey{}) != j.end()); | |
| CHECK(cj.find(DualKey{}) != cj.end()); | |
| CHECK(j.count(DualKey{}) == 1); | |
| CHECK(j.contains(DualKey{})); | |
| CHECK(j.value(DualKey{}, 0) == 1); | |
| CHECK(j.erase(DualKey{}) == 1); | |
| CHECK(!j.contains("a")); | |
| } | |
| } | |
| } | |
| #endif |