After updating to 3.2.x, deserialization seems to be broken in my project. Here is some simple code to reproduce.

Given a class:

import java.beans.ConstructorProperties;
import org.springframework.lang.NonNull;
import lombok.EqualsAndHashCode;
import lombok.Value;

@Value
@EqualsAndHashCode(callSuper = false)
public class TestClass {

  @ConstructorProperties("value")
  public TestClass(String somethingElse) {
    System.out.println("Got value: " + somethingElse);
    this.value = somethingElse;
  }

  @NonNull
  String value;
}

And a unit test:

  @Test
  void testCountryId() throws Exception {
    // Given
    var object = new TestClass("test");
    var mapper = new Jackson2ObjectMapperBuilder().build();

    // When
    var toString = mapper.writeValueAsString(object);
    var fromString = mapper.readValue(toString, TestClass.class);

    // Then
    then(fromString).isEqualTo(object);
  }

The test passes in both versions (in my actual project it fails because the value cannot be null), but

When running the unit test with Spring 3.2, it will print:

Got value: test
Got value: null

However, when running the unit test with Spring 3.1, it will print:

Got value: test
Got value: test

If I change the constructor parameter name from somethingElse to value then 3.2 prints the same as 3.1.

Comment From: bclozel

I think this is due to the addition of the parameter names module in #27511.

Rewriting the test without involving Spring at all shows the same behavior:

@Test
void testCountryId() throws Exception {
    // Given
    var object = new TestClass("test");
    var mapper = new ObjectMapper();
    mapper.registerModule(new ParameterNamesModule());

    // When
    var toString = mapper.writeValueAsString(object);
    var fromString = mapper.readValue(toString, TestClass.class);

    // Then
    then(fromString).isEqualTo(object);
    assertThat(fromString.getValue()).isEqualTo("test");
}

The behavior is described in Jackson's documentation for the module:

if class Person has a single argument constructor, its argument needs to be annotated with @JsonProperty("propertyName"). This is to preserve legacy behavior, see https://github.com/FasterXML/jackson-databind/issues/1498

Updating the class accordingly makes things work:

@Value
@EqualsAndHashCode(callSuper = false)
public class TestClass {

    @ConstructorProperties("value")
    public TestClass(@JsonProperty("value") String somethingElse) {
        Assert.notNull(somethingElse, "constructor value should not be null");
        this.value = somethingElse;
    }

    @NonNull
    String value;
}

While this behavior change comes from a Spring Framework update, I believe the behavior itself comes from Jackson and there's nothing we can do (besides removing the default registration of that module?).

Comment From: sdeleuze

Thanks @bclozel for your analysis.

This change of behavior is indeed a side effect of the Jackson module registration, but on Spring side that was not properly documented. That's now the case in 2 places: - Jackson2ObjectMapperBuilder Javadoc now documents such registration properly. - The upgrade side-effect is now properly documented here.

As a consequence, I have turned this issue into a documentation one.

Comment From: lbenedetto

I created a bug report with jackson and they seem to have accepted it as an actual issue on their end. It seems that ParameterNamesModule causes @ConstructorProperties annotations to be ignored when they shouldn't be. https://github.com/FasterXML/jackson-databind/issues/4310

Comment From: sdeleuze

Interesting, thanks for the follow up, hopefully this will be fixed on Jackson side.