# Part 5 — Polymorphic deserialisation
Post:
This part contains the most consequential correction in the whole repo, so it gets the
most space.
## The defect
The post's "Serialising a Mixed List" section shows:
```java
List payments = List.of(card, bank);
String jsonOutput = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(payments);
```
and prints output containing `"paymentType" : "credit_card"`.
It does not produce that. `writeValueAsString(Object)` sees only the runtime class of
the argument — `ImmutableCollections.ListN` — which carries no element type. Without a
declared element type Jackson never engages the polymorphic type serialiser, and the
discriminator is silently omitted from every element. No exception, no warning.
The consequence is not cosmetic. That JSON cannot be read back: the deserialiser has
no type id to dispatch on and throws `InvalidTypeIdException`. A service that writes
with the post's code and reads with the post's code does not round-trip.
## F01 — the defect and both fixes
[`F01SerialiseMixedList.java`](../src/main/java/com/ankurm/jackson3/part5polymorphic/F01SerialiseMixedList.java)
```
--- 1. single element: discriminator present ---
{"paymentType":"credit_card","amountDue":99.99,"cardNetwork":"VISA","cardNumberLastFour":"4242","paymentId":1}
--- 2. BROKEN: writeValueAsString(List) drops paymentType ---
[{"amountDue":99.99,"cardNetwork":"VISA","cardNumberLastFour":"4242","paymentId":1},{"amountDue":250.0,"bankAccountIban":"GB29NWBK60161331926819","bankName":"National Bank","paymentId":2}]
--- 3. FIX A: writerFor(TypeReference) ---
[ {
"paymentType" : "credit_card",
"amountDue" : 99.99,
"cardNetwork" : "VISA",
"cardNumberLastFour" : "4242",
"paymentId" : 1
}, {
"paymentType" : "bank_transfer",
"amountDue" : 250.0,
"bankAccountIban" : "GB29NWBK60161331926819",
"bankName" : "National Bank",
"paymentId" : 2
} ]
--- 4. FIX B: a typed array carries its component type ---
[{"paymentType":"credit_card","amountDue":99.99,"cardNetwork":"VISA","cardNumberLastFour":"4242","paymentId":1},{"paymentType":"bank_transfer","amountDue":250.0,"bankAccountIban":"GB29NWBK60161331926819","bankName":"National Bank","paymentId":2}]
```
Block 1 shows a single element serialising correctly, which is why this is easy to miss
in a unit test that only checks one object. Block 2 is the collection, discriminator
absent. Blocks 3 and 4 are the two fixes:
- `mapper.writerFor(new TypeReference>() {})` — declares the element
type on the writer.
- `payments.toArray(new PaymentMethod[0])` — an array carries its component type at
runtime, so no extra declaration is needed.
## F02 — round-trip proof
[`F02DeserialiseMixedList.java`](../src/main/java/com/ankurm/jackson3/part5polymorphic/F02DeserialiseMixedList.java)
```
Card ending: 4242
Bank: National Bank
lossy JSON round-trip -> InvalidTypeIdException
correct JSON round-trip -> 2 payments, CreditCardPayment first
```
Deserialisation itself works exactly as the post describes; it is the serialisation side
that is wrong. The middle two lines are the demonstration: lossy output fails, correctly
written output survives.
## F03 — the four include strategies
[`F03IncludeStrategies.java`](../src/main/java/com/ankurm/jackson3/part5polymorphic/F03IncludeStrategies.java)
The post gives these as a table. Here they are executed, with the read-back to confirm
each wire format is symmetric.
```
PROPERTY : {"kind":"card","amountDue":99.0}
WRAPPER_OBJECT : {"card":{"amountDue":99.0}}
WRAPPER_ARRAY : ["card",{"amountDue":99.0}]
EXISTING_PROPERTY : {"kind":"card","amountDue":99.0}
read PROPERTY -> PropCard[amountDue=99.0]
read WRAPPER_OBJECT -> WrapObjCard[amountDue=99.0]
read WRAPPER_ARRAY -> WrapArrCard[amountDue=99.0]
```
`EXISTING_PROPERTY` needs `visible = true` and a real field of that name on the type;
without it the discriminator is written but not populated back onto the object.
## F04 — bad discriminators
[`F04UnknownTypeId.java`](../src/main/java/com/ankurm/jackson3/part5polymorphic/F04UnknownTypeId.java)
```
unknown logical name -> InvalidTypeIdException
attacker-supplied class name -> InvalidTypeIdException
missing discriminator -> InvalidTypeIdException
```
All three fail the same way, which is the reassuring answer: an unregistered logical
name, an attacker-supplied fully qualified class name, and a missing discriminator are
indistinguishable to the type resolver, and none of them instantiate anything.