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 88b0da75ae8..26bd7591721 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -482,7 +482,9 @@ protected void _addImplicitConstructorCreators(DeserializationContext ctxt, final AnnotationIntrospector intr = ccState.annotationIntrospector(); final VisibilityChecker vchecker = ccState.vchecker; List implicitCtors = null; - final boolean preferPropsBased = config.getConstructorDetector().singleArgCreatorDefaultsToProperties(); + final boolean preferPropsBased = config.getConstructorDetector().singleArgCreatorDefaultsToProperties() + // [databind#3968]: Only Record's canonical constructor is allowed to be considered for properties-based creator + && !beanDesc.isRecordType(); for (CreatorCandidate candidate : ctorCandidates) { final int argCount = candidate.paramCount(); diff --git a/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordImplicitSingleValueUsePropertiesBasedCreatorsTest.java b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordImplicitSingleValueUsePropertiesBasedCreatorsTest.java new file mode 100644 index 00000000000..ff13e9b95f8 --- /dev/null +++ b/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordImplicitSingleValueUsePropertiesBasedCreatorsTest.java @@ -0,0 +1,75 @@ +package com.fasterxml.jackson.databind.records; + +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.cfg.ConstructorDetector; +import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; + +public class RecordImplicitSingleValueUsePropertiesBasedCreatorsTest extends BaseMapTest +{ + + private final ObjectMapper MAPPER = jsonMapperBuilder() + .annotationIntrospector(new Jdk8ConstructorParameterNameAnnotationIntrospector()) + .constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED) + .build(); + + record RecordWithMultiValueCanonAndSingleValueAltConstructor(int id, String name) { + + public RecordWithMultiValueCanonAndSingleValueAltConstructor(int id) { + this(id, "AltConstructor"); + } + } + + record RecordWithSingleValueCanonAndMultiValueAltConstructor(String name) { + + public RecordWithSingleValueCanonAndMultiValueAltConstructor(String name, String email) { + this("AltConstructor"); + } + } + + /* + /********************************************************************** + /* Test methods, multi-value canonical constructor + single-value alt constructor + /********************************************************************** + */ + + public void testDeserializeMultipleConstructors_UsingMultiValueCanonicalConstructor() throws Exception { + RecordWithMultiValueCanonAndSingleValueAltConstructor value = MAPPER.readValue( + a2q("{'id':123,'name':'Bob'}"), + RecordWithMultiValueCanonAndSingleValueAltConstructor.class); + + assertEquals(new RecordWithMultiValueCanonAndSingleValueAltConstructor(123, "Bob"), value); + } + + public void testDeserializeMultipleConstructors_WillIgnoreSingleValueAltConstructor() throws Exception { + RecordWithMultiValueCanonAndSingleValueAltConstructor value = MAPPER.readValue( + a2q("{'id':123}"), + RecordWithMultiValueCanonAndSingleValueAltConstructor.class); + + assertEquals(new RecordWithMultiValueCanonAndSingleValueAltConstructor(123, null), value); + } + + /* + /********************************************************************** + /* Test methods, single-value canonical constructor + multi-value alt constructor + /********************************************************************** + */ + + public void testDeserializeMultipleConstructors_UsingSingleValueCanonicalConstructor() throws Exception { + RecordWithSingleValueCanonAndMultiValueAltConstructor value = MAPPER.readValue( + a2q("{'name':'Bob'}"), + RecordWithSingleValueCanonAndMultiValueAltConstructor.class); + + assertEquals(new RecordWithSingleValueCanonAndMultiValueAltConstructor("Bob"), value); + } + + public void testDeserializeMultipleConstructors_WillIgnoreMultiValueAltConstructor() throws Exception { + try { + MAPPER.readValue(a2q("{'name':'Bob','email':'bob@email.com'}"), RecordWithSingleValueCanonAndMultiValueAltConstructor.class); + } catch (UnrecognizedPropertyException e) { + verifyException(e, "Unrecognized"); + verifyException(e, "\"email\""); + verifyException(e, "RecordWithSingleValueCanonAndMultiValueAltConstructor"); + } + } +}