Skip to content

Commit e007d71

Browse files
committed
Fix operator[](variant) ignoring NUL characters
1 parent 67a512a commit e007d71

File tree

11 files changed

+87
-46
lines changed

11 files changed

+87
-46
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ HEAD
66

77
* Forbid `deserializeJson(JsonArray|JsonObject, ...)` (issue #2135)
88
* Fix VLA support in `JsonDocument::set()`
9+
* Fix `operator[](variant)` ignoring NUL characters
910

1011
v7.2.0 (2024-09-18)
1112
------

extras/tests/JsonDocument/subscript.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,40 @@ TEST_CASE("JsonDocument::operator[]") {
1212
const JsonDocument& cdoc = doc;
1313

1414
SECTION("object") {
15-
deserializeJson(doc, "{\"hello\":\"world\"}");
15+
doc["abc"_s] = "ABC";
16+
doc["abc\0d"_s] = "ABCD";
1617

1718
SECTION("const char*") {
18-
REQUIRE(doc["hello"] == "world");
19-
REQUIRE(cdoc["hello"] == "world");
19+
REQUIRE(doc["abc"] == "ABC");
20+
REQUIRE(cdoc["abc"] == "ABC");
2021
}
2122

2223
SECTION("std::string") {
23-
REQUIRE(doc["hello"_s] == "world");
24-
REQUIRE(cdoc["hello"_s] == "world");
24+
REQUIRE(doc["abc"_s] == "ABC");
25+
REQUIRE(cdoc["abc"_s] == "ABC");
26+
REQUIRE(doc["abc\0d"_s] == "ABCD");
27+
REQUIRE(cdoc["abc\0d"_s] == "ABCD");
2528
}
2629

2730
SECTION("JsonVariant") {
28-
doc["key"] = "hello";
29-
REQUIRE(doc[doc["key"]] == "world");
30-
REQUIRE(cdoc[cdoc["key"]] == "world");
31+
doc["key1"] = "abc";
32+
doc["key2"] = "abc\0d"_s;
33+
doc["key3"] = "foo";
34+
35+
CHECK(doc[doc["key1"]] == "ABC");
36+
CHECK(doc[doc["key2"]] == "ABCD");
37+
CHECK(doc[doc["key3"]] == nullptr);
38+
CHECK(doc[doc["key4"]] == nullptr);
39+
40+
CHECK(cdoc[cdoc["key1"]] == "ABC");
41+
CHECK(cdoc[cdoc["key2"]] == "ABCD");
42+
CHECK(cdoc[cdoc["key3"]] == nullptr);
43+
CHECK(cdoc[cdoc["key4"]] == nullptr);
3144
}
3245

3346
SECTION("supports operator|") {
34-
REQUIRE((doc["hello"] | "nope") == "world"_s);
35-
REQUIRE((doc["world"] | "nope") == "nope"_s);
47+
REQUIRE((doc["abc"] | "nope") == "ABC"_s);
48+
REQUIRE((doc["def"] | "nope") == "nope"_s);
3649
}
3750

3851
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
@@ -46,7 +59,6 @@ TEST_CASE("JsonDocument::operator[]") {
4659

4760
REQUIRE(doc[vla] == "world");
4861
REQUIRE(cdoc[vla] == "world");
49-
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
5062
}
5163
#endif
5264
}

extras/tests/JsonObject/subscript.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,15 @@ TEST_CASE("JsonObject::operator[]") {
253253

254254
SECTION("JsonVariant") {
255255
obj["hello"] = "world";
256-
doc["key"] = "hello";
256+
obj["a\0b"_s] = "ABC";
257257

258-
REQUIRE(obj[obj["key"]] == "world");
259-
REQUIRE(obj[obj["foo"]] == nullptr);
258+
doc["key1"] = "hello";
259+
doc["key2"] = "a\0b"_s;
260+
doc["key3"] = "foo";
261+
262+
REQUIRE(obj[obj["key1"]] == "world");
263+
REQUIRE(obj[obj["key2"]] == "ABC");
264+
REQUIRE(obj[obj["key3"]] == nullptr);
265+
REQUIRE(obj[obj["key4"]] == nullptr);
260266
}
261267
}

extras/tests/JsonObjectConst/subscript.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
TEST_CASE("JsonObjectConst::operator[]") {
1212
JsonDocument doc;
1313
doc["hello"] = "world";
14+
doc["a\0b"_s] = "ABC";
1415
JsonObjectConst obj = doc.as<JsonObjectConst>();
1516

1617
SECTION("supports const char*") {
@@ -19,6 +20,7 @@ TEST_CASE("JsonObjectConst::operator[]") {
1920

2021
SECTION("supports std::string") {
2122
REQUIRE(obj["hello"_s] == "world"); // issue #2019
23+
REQUIRE(obj["a\0b"_s] == "ABC");
2224
}
2325

2426
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
@@ -28,13 +30,17 @@ TEST_CASE("JsonObjectConst::operator[]") {
2830
char vla[i];
2931
strcpy(vla, "hello");
3032

31-
REQUIRE("world"_s == obj[vla]);
33+
REQUIRE(obj[vla] == "world"_s);
3234
}
3335
#endif
3436

3537
SECTION("supports JsonVariant") {
36-
doc["key"] = "hello";
37-
REQUIRE(obj[obj["key"]] == "world");
38-
REQUIRE(obj[obj["foo"]] == nullptr);
38+
doc["key1"] = "hello";
39+
doc["key2"] = "a\0b"_s;
40+
doc["key3"] = "foo";
41+
REQUIRE(obj[obj["key1"]] == "world");
42+
REQUIRE(obj[obj["key2"]] == "ABC");
43+
REQUIRE(obj[obj["key3"]] == nullptr);
44+
REQUIRE(obj[obj["key4"]] == nullptr);
3945
}
4046
}

extras/tests/JsonVariant/subscript.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,19 @@ TEST_CASE("JsonVariant::operator[]") {
116116
}
117117

118118
SECTION("use JsonVariant as key") {
119-
object["a"] = "a";
120-
object["b"] = "b";
121-
object["c"] = "b";
122-
123-
REQUIRE(var[var["c"]] == "b");
124-
REQUIRE(var[var["d"]].isNull());
119+
object["a"] = "A";
120+
object["ab"] = "AB";
121+
object["ab\0c"_s] = "ABC";
122+
object["key1"] = "a";
123+
object["key2"] = "ab";
124+
object["key3"] = "ab\0c"_s;
125+
object["key4"] = "foo";
126+
127+
REQUIRE(var[var["key1"]] == "A");
128+
REQUIRE(var[var["key2"]] == "AB");
129+
REQUIRE(var[var["key3"]] == "ABC");
130+
REQUIRE(var[var["key4"]].isNull());
131+
REQUIRE(var[var["key5"]].isNull());
125132
}
126133
}
127134

extras/tests/JsonVariantConst/subscript.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,37 +50,46 @@ TEST_CASE("JsonVariantConst::operator[]") {
5050

5151
SECTION("object") {
5252
JsonObject object = doc.to<JsonObject>();
53-
object["a"] = "A";
54-
object["b"] = "B";
53+
object["ab"_s] = "AB";
54+
object["abc"_s] = "ABC";
55+
object["abc\0d"_s] = "ABCD";
5556

5657
SECTION("supports const char*") {
57-
REQUIRE("A"_s == var["a"]);
58-
REQUIRE("B"_s == var["b"]);
59-
REQUIRE(var["c"].isNull());
58+
REQUIRE(var["ab"] == "AB"_s);
59+
REQUIRE(var["abc"] == "ABC"_s);
60+
REQUIRE(var["def"].isNull());
6061
REQUIRE(var[0].isNull());
6162
}
6263

6364
SECTION("supports std::string") {
64-
REQUIRE("A"_s == var["a"_s]);
65-
REQUIRE("B"_s == var["b"_s]);
66-
REQUIRE(var["c"_s].isNull());
65+
REQUIRE(var["ab"_s] == "AB"_s);
66+
REQUIRE(var["abc"_s] == "ABC"_s);
67+
REQUIRE(var["abc\0d"_s] == "ABCD"_s);
68+
REQUIRE(var["def"_s].isNull());
6769
}
6870

6971
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
7072
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
7173
SECTION("supports VLA") {
7274
size_t i = 16;
7375
char vla[i];
74-
strcpy(vla, "a");
76+
strcpy(vla, "abc");
7577

76-
REQUIRE("A"_s == var[vla]);
78+
REQUIRE(var[vla] == "ABC"_s);
7779
}
7880
#endif
7981

8082
SECTION("supports JsonVariant") {
81-
object["c"] = "b";
82-
REQUIRE(var[var["c"]] == "B");
83-
REQUIRE(var[var["d"]].isNull());
83+
object["key1"] = "ab";
84+
object["key2"] = "abc";
85+
object["key3"] = "abc\0d"_s;
86+
object["key4"] = "foo";
87+
88+
REQUIRE(var[var["key1"]] == "AB"_s);
89+
REQUIRE(var[var["key2"]] == "ABC"_s);
90+
REQUIRE(var[var["key3"]] == "ABCD"_s);
91+
REQUIRE(var[var["key4"]].isNull());
92+
REQUIRE(var[var["key5"]].isNull());
8493
}
8594
}
8695
}

src/ArduinoJson/Document/JsonDocument.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
239239
template <typename TVariant>
240240
detail::enable_if_t<detail::IsVariant<TVariant>::value, JsonVariantConst>
241241
operator[](const TVariant& key) const {
242-
if (key.template is<const char*>())
243-
return operator[](key.template as<const char*>());
242+
if (key.template is<JsonString>())
243+
return operator[](key.template as<JsonString>());
244244
if (key.template is<size_t>())
245245
return operator[](key.template as<size_t>());
246246
return {};

src/ArduinoJson/Object/JsonObject.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
121121
// https://arduinojson.org/v7/api/jsonobject/subscript/
122122
template <typename TVariant>
123123
detail::enable_if_t<detail::IsVariant<TVariant>::value,
124-
detail::MemberProxy<JsonObject, const char*>>
124+
detail::MemberProxy<JsonObject, JsonString>>
125125
operator[](const TVariant& key) const {
126-
if (key.template is<const char*>())
127-
return {*this, key.template as<const char*>()};
126+
if (key.template is<JsonString>())
127+
return {*this, key.template as<JsonString>()};
128128
else
129129
return {*this, nullptr};
130130
}

src/ArduinoJson/Object/JsonObjectConst.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
121121
template <typename TVariant>
122122
detail::enable_if_t<detail::IsVariant<TVariant>::value, JsonVariantConst>
123123
operator[](const TVariant& key) const {
124-
if (key.template is<const char*>())
125-
return operator[](key.template as<const char*>());
124+
if (key.template is<JsonString>())
125+
return operator[](key.template as<JsonString>());
126126
else
127127
return JsonVariantConst();
128128
}

src/ArduinoJson/Variant/JsonVariantConst.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class JsonVariantConst : public detail::VariantTag,
136136
if (key.template is<size_t>())
137137
return operator[](key.template as<size_t>());
138138
else
139-
return operator[](key.template as<const char*>());
139+
return operator[](key.template as<JsonString>());
140140
}
141141

142142
// DEPRECATED: use obj[key].is<T>() instead

0 commit comments

Comments
 (0)