The following function tests if the input document is valid.

// Returns true if input points to a valid JSON string
bool validateJson(const char* input) {
  StaticJsonDocument<0> doc, filter;
  return deserializeJson(doc, json, DeserializationOption::Filter(filter)) == DeserializationError::Ok;

As you can see, it uses a null filter to skip all the values in the input. Without this filter deserializeJson() would return NoMemory.

Because deserializeJson() is skipping the values, it will oversee some errors in the input. For example, it will not detect errors in UTF-16 characters. Therefore, this solution is not perfect but probably good enough for most projects.

Thank you to Jeroen Döll for finding this technique.