diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java index 80d9d492c20..cb9cb872a5e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java @@ -977,8 +977,12 @@ protected SettableBeanProperty constructSettableProperty(DeserializationContext beanDesc.getClassAnnotations(), (AnnotatedMethod) mutator); } else { // 08-Sep-2016, tatu: wonder if we should verify it is `AnnotatedField` to be safe? - prop = new FieldProperty(propDef, type, typeDeser, - beanDesc.getClassAnnotations(), (AnnotatedField) mutator); + AnnotatedField field = (AnnotatedField) mutator; + if (field.isImmutable()) { + // [databind#3736] Pointless to create a SettableBeanProperty for an immutable field + return null; + } + prop = new FieldProperty(propDef, type, typeDeser, beanDesc.getClassAnnotations(), field); } JsonDeserializer deser = findDeserializerFromAnnotation(ctxt, mutator); if (deser == null) { diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java index be182b72bfe..cda43d644b5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java @@ -128,6 +128,14 @@ public Object getValue(Object pojo) throws IllegalArgumentException */ public boolean isTransient() { return Modifier.isTransient(getModifiers()); } + /** + * @since 2.18 + */ + public boolean isImmutable() { + // Records' fields can't mutated via reflection (JDK-8247517) + return getDeclaringClass().isRecord(); + } + @Override public int hashCode() { return Objects.hashCode(_field); diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java index 924492686a3..14481405a73 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -434,13 +434,7 @@ protected void collectAll() // First: gather basic accessors LinkedHashMap props = new LinkedHashMap(); - // 15-Jan-2023, tatu: [databind#3736] Let's avoid detecting fields of Records - // altogether (unless we find a good reason to detect them) - // 17-Apr-2023: Need Records' fields for serialization for cases - // like [databind#3628], [databind#3895] and [databind#3992] - if (!isRecordType() || _forSerialization) { - _addFields(props); // note: populates _fieldRenameMappings - } + _addFields(props); // note: populates _fieldRenameMappings _addMethods(props); // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static // inner classes, see [databind#1502] @@ -1301,10 +1295,7 @@ protected void _removeUnwantedProperties(Map props) */ protected void _removeUnwantedAccessor(Map props) { - // 15-Jan-2023, tatu: Avoid pulling in mutators for Records; Fields mostly - // since there should not be setters. - final boolean inferMutators = !isRecordType() - && _config.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS); + final boolean inferMutators = _config.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS); Iterator it = props.values().iterator(); while (it.hasNext()) {