C++ supports three types of char: signed char, unsigned char, and “naked” char.
signed char and unsigned char are integer types, but “naked” char is a character type.

JSON supports numbers and strings but has no “character” type.
If you want to store an integer, you can use signed char, unsigned char, or another integer type.
If you want to store a string, you can use const char*, String, or any supported string type.

Old versions of ArduinoJson used to treat “naked” char as an integer type, but this caused a few problems.
ArduinoJson 6.18 deprecated the support for “naked” char, and then ArduinoJson 6.20 removed it entirely.

You can write a custom converter to restore the support for “naked” char, but you must choose between two options: treat char as an integer or a character type.

Option 1: treat char as an integer type

The following converter treats char as signed char. Note that char can be signed or unsigned, depending on the compiler, so you may need to adapt this code.

namespace ArduinoJson {
template <>
struct Converter<char> {
  static void toJson(char c, JsonVariant var) {
    var.set(static_cast<signed char>(buf));
  }

  static char fromJson(JsonVariantConst src) {
    return static_cast<char>(var.as<signed char>());
  }

  static bool checkJson(JsonVariantConst src) {
    return src.is<signed char>();
  }
};
}

Option 2: treat char as a character type

The following converter stores chars as one-character strings.

namespace ArduinoJson {
template <>
struct Converter<char> {
  static void toJson(char c, JsonVariant var) {
    char buf[] = {c, 0};  // create a string of length 1
    var.set(buf);
  }

  static char fromJson(JsonVariantConst src) {
    auto p = src.as<const char*>();
    return p ? p[0] : 0;  // returns the first character or 0
  }

  static bool checkJson(JsonVariantConst src) {
    auto p = src.as<const char*>();
    return p && p[0] && !p[1];  // must be a string of length 1
  }
};
}