Deprecate containsKey() in favor of doc["key"].is<T>()

See #2121
This commit is contained in:
Benoit Blanchon
2024-09-06 17:32:09 +02:00
parent dd1d96e28f
commit d92eee8736
22 changed files with 263 additions and 269 deletions

View File

@ -9,6 +9,7 @@ HEAD
* Reduce the slot size (see table below)
* Improve message when user forgets third arg of `serializeJson()` et al.
* Set `ARDUINOJSON_USE_DOUBLE` to `0` by default on 8-bit architectures
* Deprecate `containsKey()` in favor of `doc["key"].is<T>()`
| Architecture | before | after |
|--------------|----------|----------|
@ -16,6 +17,25 @@ HEAD
| 32-bit | 16 bytes | 8 bytes |
| 64-bit | 24 bytes | 16 bytes |
> ### BREAKING CHANGES
>
> After being on the death row for years, the `containsKey()` method has finally been deprecated.
> You should replace `doc.containsKey("key")` with `doc["key"].is<T>()`, which not only checks that the key exists but also that the value is of the expected type.
>
> ```cpp
> // Before
> if (doc.containsKey("value")) {
> int value = doc["value"];
> // ...
> }
>
> // After
> if (doc["value"].is<int>()) {
> int value = doc["value"];
> // ...
> }
> ```
v7.1.0 (2024-06-27)
------

View File

@ -16,9 +16,10 @@ endif()
add_executable(DeprecatedTests
add.cpp
BasicJsonDocument.cpp
containsKey.cpp
createNestedArray.cpp
createNestedObject.cpp
BasicJsonDocument.cpp
DynamicJsonDocument.cpp
macros.cpp
memoryUsage.cpp

View File

@ -0,0 +1,210 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::containsKey()") {
JsonDocument doc;
SECTION("returns true on object") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when value is null") {
doc["hello"] = static_cast<const char*>(0);
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when key is a std::string") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello"_s) == true);
}
SECTION("returns false on object") {
doc["world"] = "hello";
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on array") {
doc.add("hello");
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on null") {
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("support JsonVariant") {
doc["hello"] = "world";
doc["key"] = "hello";
REQUIRE(doc.containsKey(doc["key"]) == true);
REQUIRE(doc.containsKey(doc["foo"]) == false);
}
}
TEST_CASE("MemberProxy::containsKey()") {
JsonDocument doc;
auto mp = doc["hello"];
SECTION("containsKey(const char*)") {
mp["key"] = "value";
REQUIRE(mp.containsKey("key") == true);
REQUIRE(mp.containsKey("key") == true);
}
SECTION("containsKey(std::string)") {
mp["key"] = "value";
REQUIRE(mp.containsKey("key"_s) == true);
REQUIRE(mp.containsKey("key"_s) == true);
}
}
TEST_CASE("JsonObject::containsKey()") {
JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = 42;
SECTION("returns true only if key is present") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("returns false after remove()") {
obj.remove("hello");
REQUIRE(false == obj.containsKey("hello"));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("key is a JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
SECTION("std::string") {
REQUIRE(true == obj.containsKey("hello"_s));
}
SECTION("unsigned char[]") {
unsigned char key[] = "hello";
REQUIRE(true == obj.containsKey(key));
}
}
TEST_CASE("JsonObjectConst::containsKey()") {
JsonDocument doc;
doc["hello"] = 42;
auto obj = doc.as<JsonObjectConst>();
SECTION("supports const char*") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("supports std::string") {
REQUIRE(false == obj.containsKey("world"_s));
REQUIRE(true == obj.containsKey("hello"_s));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}
TEST_CASE("JsonVariant::containsKey()") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("returns false is unbound") {
CHECK_FALSE(JsonVariant().containsKey("hello"));
}
SECTION("containsKey(const char*)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("containsKey(std::string)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
SECTION("containsKey(JsonVariant)") {
var["hello"] = "world";
var["key"] = "hello";
REQUIRE(var.containsKey(doc["key"]) == true);
REQUIRE(var.containsKey(doc["foo"]) == false);
}
}
TEST_CASE("JsonVariantConst::containsKey()") {
JsonDocument doc;
doc["hello"] = "world";
JsonVariantConst var = doc.as<JsonVariant>();
SECTION("support const char*") {
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("support std::string") {
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == var.containsKey(vla));
}
#endif
SECTION("support JsonVariant") {
doc["key"] = "hello";
REQUIRE(var.containsKey(var["key"]) == true);
REQUIRE(var.containsKey(var["foo"]) == false);
}
}

View File

@ -9,7 +9,6 @@ add_executable(JsonDocumentTests
clear.cpp
compare.cpp
constructor.cpp
containsKey.cpp
ElementProxy.cpp
isNull.cpp
issue1120.cpp

View File

@ -93,25 +93,6 @@ TEST_CASE("MemberProxy::operator==()") {
}
}
TEST_CASE("MemberProxy::containsKey()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("containsKey(const char*)") {
mp["key"] = "value";
REQUIRE(mp.containsKey("key") == true);
REQUIRE(mp.containsKey("key") == true);
}
SECTION("containsKey(std::string)") {
mp["key"] = "value";
REQUIRE(mp.containsKey("key"_s) == true);
REQUIRE(mp.containsKey("key"_s) == true);
}
}
TEST_CASE("MemberProxy::operator|()") {
JsonDocument doc;

View File

@ -1,54 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::containsKey()") {
JsonDocument doc;
SECTION("returns true on object") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when value is null") {
doc["hello"] = static_cast<const char*>(0);
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when key is a std::string") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello"_s) == true);
}
SECTION("returns false on object") {
doc["world"] = "hello";
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on array") {
doc.add("hello");
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on null") {
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("support JsonVariant") {
doc["hello"] = "world";
doc["key"] = "hello";
REQUIRE(doc.containsKey(doc["key"]) == true);
REQUIRE(doc.containsKey(doc["foo"]) == false);
}
}

View File

@ -5,7 +5,6 @@
add_executable(JsonObjectTests
clear.cpp
compare.cpp
containsKey.cpp
equals.cpp
isNull.cpp
iterator.cpp

View File

@ -1,39 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonObject::containsKey()") {
JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = 42;
SECTION("returns true only if key is present") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("returns false after remove()") {
obj.remove("hello");
REQUIRE(false == obj.containsKey("hello"));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("key is a JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}

View File

@ -34,13 +34,6 @@ TEST_CASE("std::string") {
REQUIRE("value"_s == obj["key"_s]);
}
SECTION("containsKey()") {
char json[] = "{\"key\":\"value\"}";
deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
REQUIRE(true == obj.containsKey("key"_s));
}
SECTION("remove()") {
JsonObject obj = doc.to<JsonObject>();
obj["key"] = "value";

View File

@ -3,7 +3,6 @@
# MIT License
add_executable(JsonObjectConstTests
containsKey.cpp
equals.cpp
isNull.cpp
iterator.cpp

View File

@ -1,40 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonObjectConst::containsKey()") {
JsonDocument doc;
doc["hello"] = 42;
auto obj = doc.as<JsonObjectConst>();
SECTION("supports const char*") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("supports std::string") {
REQUIRE(false == obj.containsKey("world"_s));
REQUIRE(true == obj.containsKey("hello"_s));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}

View File

@ -7,7 +7,6 @@ add_executable(JsonVariantTests
as.cpp
clear.cpp
compare.cpp
containsKey.cpp
converters.cpp
copy.cpp
is.cpp

View File

@ -1,36 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariant::containsKey()") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("containsKey(const char*)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("containsKey(std::string)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
SECTION("containsKey(JsonVariant)") {
var["hello"] = "world";
var["key"] = "hello";
REQUIRE(var.containsKey(doc["key"]) == true);
REQUIRE(var.containsKey(doc["foo"]) == false);
}
}

View File

@ -73,10 +73,6 @@ TEST_CASE("Unbound JsonVariant") {
CHECK_FALSE(variant["key"_s].set(1));
}
SECTION("containsKey()") {
CHECK_FALSE(variant.containsKey("hello"));
}
SECTION("remove()") {
variant.remove(0);
variant.remove("hello");

View File

@ -4,7 +4,6 @@
add_executable(JsonVariantConstTests
as.cpp
containsKey.cpp
is.cpp
isnull.cpp
nesting.cpp

View File

@ -1,41 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariantConst::containsKey()") {
JsonDocument doc;
doc["hello"] = "world";
JsonVariantConst var = doc.as<JsonVariant>();
SECTION("support const char*") {
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("support std::string") {
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == var.containsKey(vla));
}
#endif
SECTION("support JsonVariant") {
doc["key"] = "hello";
REQUIRE(var.containsKey(var["key"]) == true);
REQUIRE(var.containsKey(var["foo"]) == false);
}
}

View File

@ -176,15 +176,6 @@ TEST_CASE("unsigned char[]") {
}
#endif
SECTION("containsKey()") {
unsigned char key[] = "hello";
JsonDocument doc;
deserializeJson(doc, "{\"hello\":\"world\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(true == obj.containsKey(key));
}
SECTION("remove()") {
unsigned char key[] = "hello";

View File

@ -150,24 +150,27 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
return getVariant().template to<T>();
}
// Returns true if the root object contains the specified key.
// DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TChar>
ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0;
}
// Returns true if the root object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TString>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
const TString& key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0;
}
// Returns true if the root object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TVariant>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
const TVariant& key) const {
return containsKey(key.template as<const char*>());

View File

@ -161,27 +161,30 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
resources_);
}
// Returns true if the object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobject/containskey/
template <typename TString>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
const TString& key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// Returns true if the object contains the specified key.
// DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsonobject/containskey/
template <typename TChar>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
detail::enable_if_t<detail::IsString<TChar*>::value, bool> containsKey(
TChar* key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// Returns true if the object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobject/containskey/
template <typename TVariant>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
const TVariant& key) const {
return containsKey(key.template as<const char*>());

View File

@ -68,26 +68,29 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
return iterator();
}
// Returns true if the object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/
template <typename TString>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
const TString& key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// Returns true if the object contains the specified key.
// DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/
template <typename TChar>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// Returns true if the object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/
template <typename TVariant>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
const TVariant& key) const {
return containsKey(key.template as<const char*>());

View File

@ -139,25 +139,30 @@ class JsonVariantConst : public detail::VariantTag,
return operator[](key.template as<const char*>());
}
// Returns true if tge object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
template <typename TString>
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
const TString& key) const {
return detail::VariantData::getMember(getData(), detail::adaptString(key),
resources_) != 0;
}
// Returns true if tge object contains the specified key.
// DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
template <typename TChar>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
detail::enable_if_t<detail::IsString<TChar*>::value, bool> containsKey(
TChar* key) const {
return detail::VariantData::getMember(getData(), detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
template <typename TVariant>
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
const TVariant& key) const {
return containsKey(key.template as<const char*>());

View File

@ -165,20 +165,23 @@ class VariantRefBase : public VariantTag {
// https://arduinojson.org/v7/api/jsonvariant/subscript/
ElementProxy<TDerived> operator[](size_t index) const;
// Returns true if the object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariant/containskey/
template <typename TString>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
enable_if_t<IsString<TString>::value, bool> containsKey(
const TString& key) const;
// Returns true if the object contains the specified key.
// DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariant/containskey/
template <typename TChar>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
enable_if_t<IsString<TChar*>::value, bool> containsKey(TChar* key) const;
// Returns true if the object contains the specified key.
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariant/containskey/
template <typename TVariant>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
enable_if_t<IsVariant<TVariant>::value, bool> containsKey(
const TVariant& key) const;