Why does Jackson polymorphic serialization not work in lists?

The various reasons for why this happens are discussed here and here. I don't necessarily agree with the reasons, but Jackson, because of type erasure, doesn't off the bat know the type of elements the List (or Collection or Map) contains. It chooses to use a simple serializer that doesn't interpret your annotations.

You have two options suggested in those links:

First, you can create a class that implements List<Cat>, instantiate it appropriately and serialize the instance.

class CatList implements List<Cat> {...}

The generic type argument Cat is not lost. Jackson has access to it and uses it.

Second, you can instantiate and use an ObjectWriter for the type List<Cat>. For example

System.out.println(new ObjectMapper().writerFor(new TypeReference<List<Cat>>() {}).writeValueAsString(list));

will print

[{"@type":"cat","name":"heyo"}]

The answer Sotirios Delimanolis gave is the correct one. However, I thought it'd be nice to post this workaround as a separate answer. if you are in an environment in which you cannot change the ObjectMapper for each type of thing you need to return (like a Jersey/SpringMVC webapp), there is an alternative.

You can simply include a private final field on the class that contains the type. The field won't be visible to anything outside the class, but if you annotate it with @JsonProperty("@type") (or "@class" or whatever your type field is named) Jackson will serialize it regardless of where the object is located.

@JsonTypeName("dog")
public static class Dog implements Animal {
  @JsonProperty("@type")
  private final String type = "dog";
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}