diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 639885e3d..0d8d78558 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3667,7 +3667,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec In case the value was `null` before, it is converted to an object. @param[in] key key of the element to access - @tparam KeyT a type convertible to an object key or a `std::string_view` + @tparam KeyT a type convertible to an object key, excluding `std::string_view` @return reference to the element at key @a key @@ -3686,11 +3686,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @since version 1.0.0 */ template < class KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value&& ( + !std::is_same::type, json_pointer>::value&& #if defined(JSON_HAS_CPP_17) - std::is_same::type, std::string_view>::value || + !std::is_same::type, std::string_view>::value&& #endif - std::is_convertible::value), int >::type = 0 > + std::is_convertible::value, int >::type = 0 > reference operator[](KeyT && key) { // implicitly convert null value to an empty object @@ -3710,6 +3710,33 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } +#if defined(JSON_HAS_CPP_17) + /// @copydoc operator[](KeyT&&) + reference operator[](const std::string_view& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + if (auto it = m_value.object->find(key); it != m_value.object->end()) + { + return it->second; + } + + return set_parent(m_value.object->operator[](json::object_t::key_type(key))); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } +#endif + /*! @brief read-only access specified object element @@ -3720,7 +3747,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec undefined. @param[in] key key of the element to access - @tparam KeyT a type convertible to an object key or a `std::string_view` + @tparam KeyT a type convertible to an object key or, excluding `std::string_view` @return const reference to the element at key @a key @@ -3742,11 +3769,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @since version 1.0.0 */ template < class KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value&& ( + !std::is_same::type, json_pointer>::value&& #if defined(JSON_HAS_CPP_17) - std::is_same::type, std::string_view>::value || + std::is_same::type, std::string_view>::value&& #endif - std::is_convertible::value), int >::type = 0 > + std::is_convertible::value, int >::type = 0 > const_reference operator[](KeyT && key) const { // const operator[] only works for objects @@ -3760,6 +3787,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } +#if defined(JSON_HAS_CPP_17) + /// @copydoc operator[](KeyT&&) const + const_reference operator[](const std::string_view& key) const + { + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto it = m_value.object->find(key); + JSON_ASSERT(it != m_value.object->end()); + return it->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } +#endif + /*! @brief access specified object element diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 3b2e7840d..89c436202 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -20556,7 +20556,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec In case the value was `null` before, it is converted to an object. @param[in] key key of the element to access - @tparam KeyT a type convertible to an object key or a `std::string_view` + @tparam KeyT a type convertible to an object key, excluding `std::string_view` @return reference to the element at key @a key @@ -20575,11 +20575,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @since version 1.0.0 */ template < class KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value&& ( + !std::is_same::type, json_pointer>::value&& #if defined(JSON_HAS_CPP_17) - std::is_same::type, std::string_view>::value || + !std::is_same::type, std::string_view>::value&& #endif - std::is_convertible::value), int >::type = 0 > + std::is_convertible::value, int >::type = 0 > reference operator[](KeyT && key) { // implicitly convert null value to an empty object @@ -20599,6 +20599,33 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } +#if defined(JSON_HAS_CPP_17) + /// @copydoc operator[](KeyT&&) + reference operator[](const std::string_view& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + if (auto it = m_value.object->find(key); it != m_value.object->end()) + { + return it->second; + } + + return set_parent(m_value.object->operator[](json::object_t::key_type(key))); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } +#endif + /*! @brief read-only access specified object element @@ -20609,7 +20636,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec undefined. @param[in] key key of the element to access - @tparam KeyT a type convertible to an object key or a `std::string_view` + @tparam KeyT a type convertible to an object key or, excluding `std::string_view` @return const reference to the element at key @a key @@ -20631,11 +20658,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @since version 1.0.0 */ template < class KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value&& ( + !std::is_same::type, json_pointer>::value&& #if defined(JSON_HAS_CPP_17) - std::is_same::type, std::string_view>::value || + std::is_same::type, std::string_view>::value&& #endif - std::is_convertible::value), int >::type = 0 > + std::is_convertible::value, int >::type = 0 > const_reference operator[](KeyT && key) const { // const operator[] only works for objects @@ -20649,6 +20676,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } +#if defined(JSON_HAS_CPP_17) + /// @copydoc operator[](KeyT&&) const + const_reference operator[](const std::string_view& key) const + { + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto it = m_value.object->find(key); + JSON_ASSERT(it != m_value.object->end()); + return it->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } +#endif + /*! @brief access specified object element diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 9eb4c8824..43f879a7a 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -668,6 +668,56 @@ TEST_CASE("element access 2") CHECK(j_const[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") @@ -683,6 +733,13 @@ TEST_CASE("element access 2") CHECK_THROWS_WITH(j_const_nonobject["foo"], "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); + +#ifdef JSON_HAS_CPP_17 + CHECK_NOTHROW(j_nonobject2[std::string_view("foo")]); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); +#endif } SECTION("boolean") @@ -702,6 +759,15 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); +#endif } SECTION("string") @@ -721,6 +787,15 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); +#endif } SECTION("array") @@ -739,6 +814,15 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); +#endif } SECTION("number (integer)") @@ -758,6 +842,15 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); +#endif } SECTION("number (unsigned)") @@ -777,6 +870,15 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); +#endif } SECTION("number (floating-point)") @@ -796,6 +898,15 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); +#endif } } }