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;
}
}