Skip to content

Inconsistent behaviour with package private constructors in Jackson 3 #5427

@michael-simons

Description

@michael-simons

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

Given

package x.y;

public class ThreeValuedPackagePrivateCtor {

	private final String a;

	private final int b;

	private final Double c;

	ThreeValuedPackagePrivateCtor(String a, int b, Double c) {
		this.a = a;
		this.b = b;
		this.c = c;
	}

	public String getA() {
		return a;
	}

	public int getB() {
		return b;
	}

	public Double getC() {
		return c;
	}

}

I can map this with Jackson 2.20 from another package as this:

@Test
void shouldDeserialize() throws Exception {
	var json = """
			{
			"a": "A String",
			"b": 1,
			"c": 23.42
			}
			""";
	var om = new ObjectMapper();
	om.registerModule(new ParameterNamesModule());
	var z = om.readValue(json, ThreeValuedPackagePrivateCtor.class);
	assertThat(z.getA()).isEqualTo("A String");
	assertThat(z.getB()).isEqualTo(1);
	assertThat(z.getC()).isEqualTo(23.42);
}

Jackson 3 in the same setup fails with

tools.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `x.y.ThreeValuedPackagePrivateCtor` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: #UNKNOWN]

Throwing in a bunch of features

	@Test
	void shouldDeserialize() {
		var json = """
				{
				"a": "A String",
				"b": 1,
				"c": 23.42
				}
				""";
		var om = JsonMapper.builder()
			.enable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS)
			.enable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)
			.enable(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)
			.build();
		var z = om.readValue(json, ThreeValuedPackagePrivateCtor.class);
		assertThat(z.getA()).isEqualTo("A String");
		assertThat(z.getB()).isEqualTo(1);
		assertThat(z.getC()).isEqualTo(23.42);
	}

does not help.

I also tried various variants of .constructorDetector() without success.

Looked at those tickets / changes already:

#5246
#5308
#5318

I was able to fix my problem with @JsonCreator (original problem is reading Spring Boot Metrics endpoints, see https://github.com/neo4j/neo4j-jdbc/blob/76201e59488bd2f6fe5031b1c76500f5cfedb915/neo4j-jdbc-it/spring-boot-smoke-tests/src/test/java/org/neo4j/jdbc/it/sb/ApplicationIT.java#L87, for which I know have this… https://github.com/neo4j/neo4j-jdbc/blob/76201e59488bd2f6fe5031b1c76500f5cfedb915/neo4j-jdbc-it/spring-boot-smoke-tests/src/test/java/org/neo4j/jdbc/it/sb/ApplicationIT.java#L121 which is fine for me, but I'm sure this will cause issue somewhere else. The metrics data classes are the same like the ones above: Package private, non default constructor, no other constructor )

Version Information

Working: Jackson 2.20.1, Failing Jackson 3.0.2.
Plain Java 25, no Framework. See attached reproducer:

jackson_fun.zip

Reproduction

See reproducer, 2nd commit.

Expected behavior

See reproducer, 1st commit.

Additional context

Funny enough, quite similar class, like this:

package x.y;

public class OneValuedPackagePrivateCtor {

	private final String a;

	OneValuedPackagePrivateCtor(String a) {
		this.a = a;

	}

	public String getA() {
		return a;
	}


}

will map just fine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions