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.