-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Describe the bug
Starting with Jackson Databind 2.13.0, deserializing a polymorphic type with a default implementation (JsonTypeInfo.defaultImpl
) and an explicit null
for the type ID will produce an instance of the concrete type in which the type ID is null
instead of a value provided during construction (in its constructor/@JsonCreator
method).
In Jackson Databind 2.12.5 and earlier, this produced an instance of the concrete type in which the type ID field had the value assigned in the constructor/@JsonCreator
method.
We're using Jackson Databind to deserialize user-provided input, so unfortunately we cannot prevent them from sending JSON payloads with null
in the type ID field, even if it isn't valid in the first place.
Version information
Working on Jackson Databind 2.12.5.
Failing on Jackson Databind 2.13.0.
To Reproduce
class PolymorphicTest {
@Test
void deserializingPolymorphicTypeFillsTypeInfoField() throws JsonProcessingException {
String s = "{\"@type\": null, \"custom\": \"value\"}";
final BaseClass object = new ObjectMapper().readValue(s, BaseClass.class);
assertTrue(object instanceof TypeA);
assertEquals(TypeA.TYPE, object.type);
assertEquals("value", ((TypeA) object).customA);
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, defaultImpl = TypeA.class)
@JsonSubTypes({
@JsonSubTypes.Type(value = TypeA.class, name = TypeA.TYPE),
@JsonSubTypes.Type(value = TypeB.class, name = TypeB.TYPE)
})
static abstract class BaseClass {
@JsonTypeId
@JsonProperty("@type")
final String type;
@JsonCreator
BaseClass(@JsonProperty("@type") String type) {
this.type = type;
}
}
static class TypeA extends BaseClass {
static final String TYPE = "A";
String customA;
@JsonCreator
TypeA(@JsonProperty("custom") String custom) {
super(TYPE);
this.customA = custom;
}
}
static class TypeB extends BaseClass {
static final String TYPE = "B";
String customB;
@JsonCreator
TypeB(@JsonProperty("custom") String custom) {
super(TYPE);
this.customB = custom;
}
}
}
Expected behavior
With Jackson Databind 2.12.5, the unit test succeeds while with Jackson Databind 2.13.0 it fails because object.type
is null
instead of using the value of the default implementation TypeA
("A"):
org.opentest4j.AssertionFailedError: expected: <A> but was: <null>
at app//org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at app//org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at app//org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at app//org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at app//org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1141)
at app//jackson.polymorphic.bug.PolymorphicTest.deserializingPolymorphicTypeFillsTypeInfoField(PolymorphicTest.java:24)
Additional context
I suspect this behavior was changed in the context of #3271 and 32eee1b.
If the new behavior is intentional, it should be mentioned in the release notes for Jackson 2.13.0 as a breaking change.