I am running a project where I have multiple ESP32s communicate over WebSockets and I am using the Arduino IDE 2.3.4 for it. The communication primarily uses JSON and sends it as strings. To work with the JSON in my C++ code, I use ArduinoJson 7.3.0.
When trying to assign the value at some key in a JsonDocument to a variable and the key doesn't exists, the behavior should be that a default value is returned.
JsonVariant::as<T>()returns the value pointed by theJsonVariantcast to the specified type.This function returns a default value if the cast is not possible. The default value is:
0for numerical typesNULLforconst char*- A null reference for
JsonArrayandJsonObject.
The quote doesn't specifically mention string types, but I would have assumed that it would behave as if it would initialize the string class with an empty string. This seems to hold true for std::string_view and ArduinoJsons JsonString. std::string has the value "null", which is acceptable, too.
However, using Arduinos String doesn't work. Somehow an exception gets thrown which causes the ESP32 to reboot. Using a catch-all handler to at least stop the ESP32 from rebooting doesn't work either.
Below is some example code to test it and the resulting output.
#include <ArduinoJson.h>
#include <string_view>
#include <string>
template <typename T>
void print(const T& t) {
const char* str = "(empty)";
if constexpr (std::is_same_v<T, const char*>) {
str = t ? t : str;
} else if constexpr (std::is_same_v<T, String>) {
str = t.length() ? t.c_str() : str;
} else if constexpr (std::is_same_v<T, std::string_view>) {
str = !t.empty() ? t.data() : str;
} else {
str = t.size() ? t.c_str() : str;
}
Serial.print(str);
}
template <typename T>
void testImpl(const char* name, JsonDocument& doc) {
Serial.printf("Testing type %s\n", name);
delay(1'000);
JsonVariant v = doc.as<JsonVariant>();
T val = v["key"].as<T>();
print(val);
Serial.println("\t> worked fine!");
delay(1'000);
}
void setup() {
Serial.begin(115'200);
}
void loop() {
Serial.println("Testing access of invalid key with different string types");
JsonDocument doc;
const DeserializationError error = deserializeJson(doc, "{\"foo\":0}");
if (error) {
Serial.printf("deserializeJson() failed: %s\n", error.c_str());
} else {
#define TEST(T) testImpl<T>(#T, doc)
TEST(const char*);
TEST(std::string_view);
TEST(std::string);
TEST(JsonString);
TEST(String);
}
}
Testing access of invalid key with different string types
Testing type const char*
(empty) > worked fine!
Testing type std::string_view
(empty) > worked fine!
Testing type std::string
null > worked fine!
Testing type JsonString
(empty) > worked fine!
Testing type String
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC : 0x400861c9 PS : 0x00060730 A0 : 0x800d3c91 A1 : 0x3ffb2050
A2 : 0x00000000 A3 : 0xfffffffc A4 : 0x000000ff A5 : 0x0000ff00
A6 : 0x00ff0000 A7 : 0xff000000 A8 : 0x00000000 A9 : 0x3ffb2020
A10 : 0x3ffb20ac A11 : 0x00000000 A12 : 0x00000000 A13 : 0x00000000
A14 : 0x3ffc2190 A15 : 0x3ffb20ac SAR : 0x0000001b EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000000 LBEG : 0x400861c9 LEND : 0x400861d9 LCOUNT : 0xffffffff
Backtrace: 0x400861c6:0x3ffb2050 0x400d3c8e:0x3ffb2060 0x400d2985:0x3ffb2080 0x400d3451:0x3ffb21b0 0x400d4f8c:0x3ffb2270 0x40088c51:0x3ffb2290
I am wondering if the result of accessing an invalid key is just undefined, I am doing something wrong, or if this might be a bug in the ArduinoJson library or somewhere else.
I know I could test if it exists first or use a const char* and test if it is a nullptr, but that just adds additional steps.