Polymorphic deserialisation is the technique of deserialising a JSON payload into one of several possible concrete subtypes based on information embedded in the JSON itself. It is essential whenever your API works with inheritance hierarchies — event systems, payment processors, notification pipelines, or any domain where a list of heterogeneous objects needs to round-trip through JSON. Jackson handles this cleanly with @JsonTypeInfo and @JsonSubTypes.
Runnable code: every example below is in the jackson3-by-example repository, compiled and executed against Jackson 3.2.1 on JDK 21 — F01 serialising a mixed list, F02 deserialising it, F03 all four include strategies and F04 bad discriminators, with captured output in docs/part5-polymorphic.md.
One warning up front, because it is the single easiest way to lose an afternoon here: passing a List to writeValueAsString silently drops the type discriminator. A List carries no element type at runtime, so Jackson never engages the polymorphic type serialiser, and the resulting JSON cannot be read back. A single object serialises correctly, which is why a unit test that checks one element will not catch it. The serialisation section below shows the two fixes.
The Problem: Deserialising an Abstract Type
Consider an API that returns a list of payment methods. Some are credit cards, others are bank transfers. Both share a common base, but each has its own fields. Without polymorphic support, Jackson cannot decide which concrete class to instantiate when it sees a generic PaymentMethod type.
Setting Up the Hierarchy with @JsonTypeInfo and @JsonSubTypes
Annotate the base class (or interface) with @JsonTypeInfo to tell Jackson where the type discriminator lives in the JSON, and with @JsonSubTypes to register the permitted subtypes:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // Use a logical name as the discriminator
include = JsonTypeInfo.As.PROPERTY, // Embed it as a field in the JSON object
property = "paymentType" // The JSON key that carries the type name
)
@JsonSubTypes({
@JsonSubTypes.Type(value = CreditCardPayment.class, name = "credit_card"),
@JsonSubTypes.Type(value = BankTransferPayment.class, name = "bank_transfer")
})
public abstract class PaymentMethod {
private Long paymentId;
private double amountDue;
// Getters and setters
}