Search before asking
- [x] I searched in the issues and found nothing similar.
Describe the bug
Since version 2.18.4 (probably since https://github.com/FasterXML/jackson-databind/issues/4628) behavior of annotation on methods of a records has changed.
before 2.18.4, If I had
public record TestData(
@JsonProperty("test_property")
String value) {
@JsonIgnore
public Optional<String> getValue() {
return Optional.ofNullable(value);
}
}
and serialized object
{ "test_property": "jackson" }
it would be deserialized to TestData("jackson")
Now since 2.18.4, the @JsonIgnore
is applied to the value field even though getValue
is not the record getter. This make TestData
to be deserialized to TestData(null)
It seems that Jackson confuse the getValue()
for the record getter value()
. If I name my method something else than getX
the @JsonIgnore
is not applied to the field.
What make me think that its a bug is that an equivalent case with a class doesn't have the same behavior. If I have a class getter with @JsonIgnore
the object mapper won't apply the ignore to the class field but only the method.
Version Information
2.18.4 and 2.19.0
Reproduction
package com.libon.jackson;
import static org.assertj.core.api.Assertions.assertThat;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
class JacksonTest {
private static final ObjectMapper MAPPER = new ObjectMapper().disable(FAIL_ON_UNKNOWN_PROPERTIES);
@Test
void should_deserialize_json_to_test_data() throws Exception {
String json = """
{"test_property":"test value"}
""";
var testData = MAPPER.readValue(json, JacksonTest.TestData.class);
assertThat(testData.value()).isEqualTo("test value");
}
@Test
void should_deserialize_json_to_test_data_class() throws Exception {
String json = """
{"test_property":"test value"}
""";
var testData = MAPPER.readValue(json, JacksonTest.TestDataClass.class);
assertThat(testData.getValue()).contains("test value");
}
@Test
void should_deserialize_json_to_test_data_alternate() throws Exception {
String json = """
{"test_property":"test value"}
""";
var testData = MAPPER.readValue(json, JacksonTest.TestDataAlternate.class);
assertThat(testData.value()).isEqualTo("test value");
}
@Test
void should_not_deserialize_wrong_json_model_to_test_data() throws Exception {
String json = """
{"value":"test value"}
""";
JacksonTest.TestData testData = MAPPER.readValue(json, JacksonTest.TestData.class);
assertThat(testData.value()).isNull();
}
public record TestData(
@JsonProperty("test_property")
String value) {
@JsonIgnore
public Optional<String> getValue() {
return Optional.ofNullable(value);
}
}
public record TestDataAlternate(
@JsonProperty("test_property")
String value) {
@JsonIgnore
public Optional<String> optionalValue() {
return Optional.ofNullable(value);
}
}
public static final class TestDataClass {
private final String value;
public TestDataClass(
@JsonProperty("test_property")
String value) {
this.value = value;
}
@JsonIgnore
public Optional<String> getValue() {
return Optional.ofNullable(value);
}
}
}
Expected behavior
The @JsonIgnore
on a record method should not be applied to the record field especially when its not the field getter.
Additional context
No response
Comment From: JooHyukKim
Thank you for your report!
I tried running tests provided and only should_deserialize_json_to_test_data
fails.
Is that what you're expecting @emouty ?
Comment From: JooHyukKim
Filed #5185 for reproduction
Comment From: emouty
Yes, the others are just here to demonstrate the different behaviours
Comment From: cowtowncoder
Right, the issue is that even though 2 accessors are related (getValue()
is recognized as getter for property "value"; and Record field has "implicit" name of "value"), they are not considered a properly "split" case (one accessor to be ignore, other included).