Доступ к неверному ключу в ArduinoJson приводит к перезагрузке ESP32

Я работаю над проектом, в котором несколько плат ESP32 взаимодействуют через WebSockets. Для этого я использую Arduino IDE 2.3.4. В основном, обмен данными осуществляется через JSON, данные передаются в виде строк. Для работы с JSON в моём коде на C++ я использую ArduinoJson 7.3.0.

При попытке присвоить значение некоторому ключу в JsonDocument переменной, если ключ не существует, должно возвращаться значение по умолчанию.

JsonVariant::as<T>():

JsonVariant::as<T>() возвращает значение, на которое указывает JsonVariant, приведенный к указанному типу.

Эта функция возвращает значение по умолчанию, если приведение типа невозможно. Значение по умолчанию:

  • 0 для числовых типов
  • NULL для const char*
  • Пустая ссылка для JsonArray и JsonObject.

В цитате не упоминаются строковые типы, но я бы предположил, что это будет выглядеть так, будто класс string инициализируется пустой строкой. Похоже, это справедливо для std::string_view и ArduinoJsons JsonString. std::string имеет значение "null", что тоже приемлемо.

Однако использование Arduino String не работает. Каким-то образом возникает исключение, которое приводит к перезагрузке ESP32. Использование обработчика событий, чтобы хотя бы предотвратить перезагрузку ESP32, тоже не работает.

Ниже приведен пример кода для тестирования и полученный результат.

#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

Мне интересно, является ли результат доступа к недопустимому ключу просто неопределенным, я делаю что-то неправильно или это может быть ошибка в библиотеке ArduinoJson или где-то еще.

Я знаю, что мог бы сначала проверить, существует ли он, или использовать const char* и проверить, является ли он nullptr, но это просто добавляет дополнительные шаги.

, 👍0

Обсуждение

Вы также можете использовать containsKey... Но, как правило, при работе с указателями необходимо учитывать nullptr. Или использовать исключения, если они включены., @KIIV

Это не воспроизводится здесь на ESP32-S3 под управлением ESP32 Arduino core v3.1.1 и ArduinoJson v7.3.0, @timemage

@timemage Удивительно, но тот же самый код с той же версией Arduino IDE, ядром ESP32 и ArduinoJson, даже с тем же ESP32, не выдал ошибку при загрузке скетча с моего ноутбука., @Joel


1 ответ


Лучший ответ:

2

Похоже, это одна из недавних проблем с классом String в ядре Arduino для ESP32.
Пожалуйста, проверьте:

  • Сбой при использовании String::move для пустой строки #10938
  • Сбой в serializeJson с пустыми значениями после обновления № 10971
,