JsonVariant, which is the type that hold the values in JsonArray and JsonObject, provides the method is<T>() that returns true if the value is of type T.

Type of a value in an object

object["key"] = "value";
object["key"].is<const char*>(); // true
object["key"].is<int>(); // false
object["key"].is<JsonObject&>(); // false

Type of a value in an array

array.add(42);
array[0].is<const char*>(); //  false
array[0].is<int>(); //  true
array[0].is<JsonObject&>(); //  false

In object iteration loop

To get the type of a value while iterating an object:

for (auto kvp : object) {
    const char* key = kvp.key;
    if (kvp.value.is<int>()) {
        int value = kvp.value.as<int>();
        // ...
    } else if (kvp.value.is<char*>()) {
        const char* value = kvp.value.as<char*>();
        // ...
    } else {
        // ...
    }
}
for (JsonObject::iterator it=object.begin(); it!=object.end(); ++it) {
    const char* key = it->key;
    if (it->value.is<int>()) {
        int value = it->value.as<int>();
        // ...
    } else if (value.is<char*>()) {
        const char* value = it->value.as<char*>();
        // ...
    } else {
        // ...
    }
}

In array iteration loop

for (auto value : array) {
    if (value.is<int>()) {
        int value = value.as<int>();
        // ...
    } else if (value.is<char*>()) {
        const char* value = value.as<char*>();
        // ...
    } else {
        // ...
    }
}
for (JsonArray::iterator it=array.begin(); it!=array.end(); ++it) {
    if (it->is<int>()) {
        int value = it->as<int>();
        // ...
    } else if (it->is<char*>()) {
        const char* value = it->as<char*>();
        // ...
    } else {
        // ...
    }
}

See also

In the “Case Studies” chapter of Mastering ArduinoJson, there is a complete example that shows how to display the type and value of all the members of an object, and of all its children recursively.

Global warming stripes by Professor Ed Hawkins (University of Reading)