Updated Answer - Best parts of all other answers
I am describing solutions for various use cases and would be addressing the infinite recursion problem as well
- Case 1: You are in control of the classes, i.e, you get to write your own - Cat,- Dogclasses as well as the- IAnimalinterface. You can simply follow the solution provided by @marcus-junius-brutus(the top rated answer)
 - There won't be any infinite recursion if there is a common base interface as - IAnimal
 - But, what if I don't want to implement the - IAnimalor any such interface?
 - Then, @marcus-junius-brutus(the top rated answer) will produce an infinite recursion error. In this case, we can do something like below. - We would have to create a copy constructor inside the base class and a wrapper subclass as follows: 
.
// Base class(modified)
public class Cat implements IAnimal {
    public String name;
    public Cat(String name) {
        super();
        this.name = name;
    }
    // COPY CONSTRUCTOR
    public Cat(Cat cat) {
        this.name = cat.name;
    }
    @Override
    public String sound() {
        return name + " : \"meaow\"";
    };
}
    // The wrapper subclass for serialization
public class CatWrapper extends Cat{
    public CatWrapper(String name) {
        super(name);
    }
    public CatWrapper(Cat cat) {
        super(cat);
    }
}
And the serializer for the type Cat:
public class CatSerializer implements JsonSerializer<Cat> {
    @Override
    public JsonElement serialize(Cat src, Type typeOfSrc, JsonSerializationContext context) {
        // Essentially the same as the type Cat
        JsonElement catWrapped = context.serialize(new CatWrapper(src));
        // Here, we can customize the generated JSON from the wrapper as we want.
        // We can add a field, remove a field, etc.
        return modifyJSON(catWrapped);
    }
    private JsonElement modifyJSON(JsonElement base){
        // TODO: Modify something
        return base;
    }
}
So, why a copy constructor?
Well, once you define the copy constructor, no matter how much the base class changes, your wrapper will continue with the same role. Secondly, if we don't define a copy constructor and simply subclass the base class then we would have to "talk" in terms of the extended class, i.e, CatWrapper. It is quite possible that your components talk in terms of the base class and not the wrapper type.
Is there an easy alternative?
Sure, it has now been introduced by Google - this is the RuntimeTypeAdapterFactory implementation:
RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");
Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
    .create();
Here, you would need to introduce a field called "type" in Animal and the value of the same inside Dog to be "dog", Cat to be "cat"
Complete example: https://static.javadoc.io/org.danilopianini/gson-extras/0.2.1/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.html
.
// The class we are NOT allowed to modify
public class Dog implements IAnimal {
    public String name;
    public int ferocity;
    public Dog(String name, int ferocity) {
        super();
        this.name = name;
        this.ferocity = ferocity;
    }
    @Override
    public String sound() {
        return name + " : \"bark\" (ferocity level:" + ferocity + ")";
    }
}
// The marker interface
public interface AnimalInterface {
}
// The subclass for serialization
public class DogWrapper  extends Dog implements AnimalInterface{
    public DogWrapper(String name, int ferocity) {
        super(name, ferocity);
    }
}
// The subclass for serialization
public class CatWrapper extends Cat implements AnimalInterface{
    public CatWrapper(String name) {
        super(name);
    }
}
So, we would be using CatWrapper instead of Cat, DogWrapper instead of Dog and 
AlternativeAnimalAdapter instead of IAnimalAdapter
// The only difference between `IAnimalAdapter` and `AlternativeAnimalAdapter` is that of the interface, i.e, `AnimalInterface` instead of `IAnimal`
public class AlternativeAnimalAdapter implements JsonSerializer<AnimalInterface>, JsonDeserializer<AnimalInterface> {
    private static final String CLASSNAME = "CLASSNAME";
    private static final String INSTANCE  = "INSTANCE";
    @Override
    public JsonElement serialize(AnimalInterface src, Type typeOfSrc,
                                 JsonSerializationContext context) {
        JsonObject retValue = new JsonObject();
        String className = src.getClass().getName();
        retValue.addProperty(CLASSNAME, className);
        JsonElement elem = context.serialize(src); 
        retValue.add(INSTANCE, elem);
        return retValue;
    }
    @Override
    public AnimalInterface deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext context) throws JsonParseException  {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
        String className = prim.getAsString();
        Class<?> klass = null;
        try {
            klass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new JsonParseException(e.getMessage());
        }
        return context.deserialize(jsonObject.get(INSTANCE), klass);
    }
}
We perform a test:
public class Test {
    public static void main(String[] args) {
        // Note that we are using the extended classes instead of the base ones
        IAnimal animals[] = new IAnimal[]{new CatWrapper("Kitty"), new DogWrapper("Brutus", 5)};
        Gson gsonExt = null;
        {
            GsonBuilder builder = new GsonBuilder();
            builder.registerTypeAdapter(AnimalInterface.class, new AlternativeAnimalAdapter());
            gsonExt = builder.create();
        }
        for (IAnimal animal : animals) {
            String animalJson = gsonExt.toJson(animal, AnimalInterface.class);
            System.out.println("serialized with the custom serializer:" + animalJson);
            AnimalInterface animal2 = gsonExt.fromJson(animalJson, AnimalInterface.class);
        }
    }
}
Output:
serialized with the custom serializer:{"CLASSNAME":"com.examples_so.CatWrapper","INSTANCE":{"name":"Kitty"}}
serialized with the custom serializer:{"CLASSNAME":"com.examples_so.DogWrapper","INSTANCE":{"name":"Brutus","ferocity":5}}