Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1560,13 +1560,19 @@ protected void _renameProperties(Map<String, POJOPropertyBuilder> 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<PropertyName> l = prop.findExplicitNames();

// no explicit names? Implicit one is fine as is
if (l.isEmpty()) {
continue;
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cowtowncoder just a nitpick but here I think the test is too ambiguous, it would have been better to set original's prop to a different value than the default one, to validate that it's indeed the default value that is used and not the one coming from json.

I can submit a PR if you want :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha! I tried and actually the comment

// Since setter is ignored, the deserialized object should have the default value

Is incorrect, if I remove the default value from the class and set it manually just before calling writeValueAsString, then result.getProp() has the value "someValue".

I tested on 2.17 too and it was like that also.

Copy link
Contributor

@victornoel victornoel Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this behaviour is actually consistent with when we don't rename the prop (cf my examples in #5398 (comment)). I proposed an improvement of this test here: #5412

}
}