From 4944befa3d99a04e98b0610f42d2910b4c8eaf21 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 19 Nov 2025 17:07:57 -0800 Subject: [PATCH] Fix #5398: ignore + rename --- release-notes/VERSION-2.x | 4 ++ .../introspect/POJOPropertiesCollector.java | 12 +++-- .../JsonPropertyRename5398Test.java | 46 +++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/introspect/JsonPropertyRename5398Test.java diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 43c7cac900..b37271dcfb 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -19,6 +19,10 @@ Project: jackson-databind customizability (contributed by @wrongwrong) #5361: Fix Maven SBOM publishing +#5398: `@JsonProperty.value` + `@JsonIgnore` on setter does not work + anymore since 2.18.4 + (reported by @victor-noel-pfx) + (fix by @cowtowncoder, w/ Claude code) 2.20.2 (not yet released) 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 b9cc0c6e13..d7dd6ac7c9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java @@ -1560,13 +1560,19 @@ protected void _renameProperties(Map props) POJOPropertyBuilder prop = entry.getValue(); // 10-Apr-2025: [databind#4628] skip properties that are marked to be ignored - // TODO: we are using implicit name, is that ok? + // 19-Nov-2025: [databind#5398] BUT do not skip if property has explicit names + // on accessors that are NOT ignored (e.g., @JsonProperty on getter but @JsonIgnore on setter). + // NOTE: For Records we need to be more conservative as constructor parameters may have + // both annotations but generated accessors don't always inherit @JsonIgnore if (_ignoredPropertyNames != null && _ignoredPropertyNames.contains(prop.getName())) { - continue; + // For Records: always skip (safer due to annotation inheritance issues) + // For regular classes: only skip if NO explicit names on non-ignored accessors + if (isRecordType() || !prop.anyExplicitsWithoutIgnoral()) { + continue; + } } Collection l = prop.findExplicitNames(); - // no explicit names? Implicit one is fine as is if (l.isEmpty()) { continue; diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/JsonPropertyRename5398Test.java b/src/test/java/com/fasterxml/jackson/databind/introspect/JsonPropertyRename5398Test.java new file mode 100644 index 0000000000..5d7c2bf863 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/introspect/JsonPropertyRename5398Test.java @@ -0,0 +1,46 @@ +package com.fasterxml.jackson.databind.introspect; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; + +import static org.junit.jupiter.api.Assertions.*; + +// [databind#5398] @JsonProperty on getter with @JsonIgnore on setter +// causes deserialization to fail since 2.18.4 +public class JsonPropertyRename5398Test extends DatabindTestUtil +{ + static class Test5398 { + private String prop = "someValue"; + + @JsonProperty(value = "renamedProp") + public String getProp() { + return prop; + } + + @JsonIgnore + public void setProp(String prop) { + this.prop = prop; + } + } + + private final ObjectMapper MAPPER = newJsonMapper(); + + @Test + public void testRenamedPropertyWithIgnoredSetter5398() throws Exception + { + Test5398 original = new Test5398(); + String json = MAPPER.writeValueAsString(original); + + // Should serialize with renamed property + assertEquals("{\"renamedProp\":\"someValue\"}", json); + + // Should be able to deserialize back (setter is ignored, so field remains default) + Test5398 result = MAPPER.readValue(json, Test5398.class); + assertNotNull(result); + // Since setter is ignored, the deserialized object should have the default value + assertEquals("someValue", result.getProp()); + } +}