-
-
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.