diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index edd89ca848..879cf86d0a 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -849,6 +849,8 @@ Carter Kozak (carterkozak@github) * Contributed #3876: `TypeFactory` cache performance degradation with `constructSpecializedType()` (2.15.0) + * Contributed #4688: Should allow deserializing with no-arg `@JsonCreator(mode = DELEGATING)` + (2.18.0) Reinhard Prechtl (dnno@github) * Reported #2034: Serialization problem with type specialization of nested generic types diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 70fa70f88a..717bee8fde 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -70,6 +70,8 @@ Project: jackson-databind (contributed by Sim Y-T) #4678: Java records don't serialize with `MapperFeature.REQUIRE_SETTERS_FOR_GETTERS` (reported by Mathijs V) +#4688: Should allow deserializing with no-arg `@JsonCreator(mode = DELEGATING)` + (contributed by Carter K) 2.17.2 (05-Jul-2024) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java index ec99b8f7f7..8c262cf406 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -468,6 +468,13 @@ private boolean _addExplicitDelegatingCreator(DeserializationContext ctxt, int ix = -1; final int argCount = candidate.paramCount(); SettableBeanProperty[] properties = new SettableBeanProperty[argCount]; + // [databind#4688]: Should still accept 0-arg (explicitly delegated) creator + // for backwards-compatibility (worked in 2.17 and before) + if (argCount == 0) { + // "Convert" to property-based since that works well + creators.addPropertyCreator(candidate.creator(), true, properties); + return true; + } for (int i = 0; i < argCount; ++i) { AnnotatedParameter param = candidate.parameter(i); JacksonInject.Value injectId = candidate.injection(i); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingletonDelegatingCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingletonDelegatingCreatorTest.java new file mode 100644 index 0000000000..669e0c4719 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/creators/SingletonDelegatingCreatorTest.java @@ -0,0 +1,78 @@ +package com.fasterxml.jackson.databind.deser.creators; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; + +import static org.junit.jupiter.api.Assertions.assertSame; + +// [databind#4688] +public class SingletonDelegatingCreatorTest extends DatabindTestUtil +{ + static final class NoFieldSingletonWithDelegatingCreator { + static final NoFieldSingletonWithDelegatingCreator INSTANCE = new NoFieldSingletonWithDelegatingCreator(); + + private NoFieldSingletonWithDelegatingCreator() {} + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + static NoFieldSingletonWithDelegatingCreator of() { + return INSTANCE; + } + } + + static final class NoFieldSingletonWithPropertiesCreator { + static final NoFieldSingletonWithPropertiesCreator INSTANCE = new NoFieldSingletonWithPropertiesCreator(); + + private NoFieldSingletonWithPropertiesCreator() {} + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + static NoFieldSingletonWithPropertiesCreator of() { + return INSTANCE; + } + } + + static final class NoFieldSingletonWithDefaultCreator { + static final NoFieldSingletonWithDefaultCreator INSTANCE = new NoFieldSingletonWithDefaultCreator(); + + private NoFieldSingletonWithDefaultCreator() {} + + @JsonCreator + static NoFieldSingletonWithDefaultCreator of() { + return INSTANCE; + } + } + + /* + /********************************************************************** + /* Test methods + /********************************************************************** + */ + + private final ObjectMapper MAPPER = newJsonMapper(); + + @Test + public void testNoFieldSingletonWithDelegatingCreator() throws Exception + { + NoFieldSingletonWithDelegatingCreator deserialized = MAPPER.readValue("{}", + NoFieldSingletonWithDelegatingCreator.class); + assertSame(NoFieldSingletonWithDelegatingCreator.INSTANCE, deserialized); + } + + @Test + public void testNoFieldSingletonWithPropertiesCreator() throws Exception + { + NoFieldSingletonWithPropertiesCreator deserialized = MAPPER.readValue("{}", + NoFieldSingletonWithPropertiesCreator.class); + assertSame(NoFieldSingletonWithPropertiesCreator.INSTANCE, deserialized); + } + + @Test + public void testNoFieldSingletonWithDefaultCreator() throws Exception + { + NoFieldSingletonWithDefaultCreator deserialized = MAPPER.readValue("{}", + NoFieldSingletonWithDefaultCreator.class); + assertSame(NoFieldSingletonWithDefaultCreator.INSTANCE, deserialized); + } +}