Sylvain Lecoy Sylvain Lecoy - 28 days ago 5
Java Question

Jackson single argument constructor with single argument fails with ParameterNameModule

I am using Jackson 2.8.5 with ParameterNamesModule for Java 8 (https://github.com/FasterXML/jackson-modules-java8).

My problem is very specific for one use case, when I want to de-serialize a class with a single constructor using a single argument. Here is a test to reproduces the behavior:

public class JacksonTest {

@Test
public void TestReadValue() throws IOException {
ObjectMapper objectMapper = new ObjectMapper()
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));

ImmutableIdentity identity = objectMapper.readValue("{\"id\":\"ABCDEF\"}", ImmutableIdentity.class);

assertEquals("ABCDEF", identity.id);
}

private static final class ImmutableIdentity {

private final String id;

public ImmutableIdentity(final String id) {
Objects.requireNonNull(id, "The id must not be null.");

this.id = id;
}
}

}


The test fails with the reason:


com.fasterxml.jackson.databind.JsonMappingException: Can not construct
instance of JacksonTest$ImmutableIdentity, problem: The id must not be
null. at [Source: {"id":"ABCDEF"}; line: 1, column: 15]


The funny thing is that if I add another argument to the constructor, the test passes OK.

public class JacksonTest {

@Test
public void TestReadValue() throws IOException {
ObjectMapper objectMapper = new ObjectMapper()
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));

ImmutableIdentity identity = objectMapper.readValue("{\"id\":\"ABCDEF\"}", ImmutableIdentity.class);

assertEquals("ABCDEF", identity.id);
}

private static final class ImmutableIdentity {

private final String id;

public ImmutableIdentity(final String id, **final String unused**) {
Objects.requireNonNull(id, "The id must not be null.");

this.id = id;
}
}

}


I really don't like the idea to use a useless argument in the constructor here to make it less ambiguous, because it has no value in my business objects, especially they are for instance ProjectId, or some abstract Id that defines my entities and I need to construct them manually as well. So I would like to find a configuration of Jackson to support this but I could not.

I also crossposted here for the maintainers: https://github.com/FasterXML/jackson-modules-java8/issues/8

Answer Source

Are you by any chance compiling JacksonTest with -parameters option? If so, this is expected behavior. Single argument constructors were historically used as delegating creators. I've had discussions about this matter with @staxman even when we created the module. The issue popped up a number of times by various users, see this issue for details. Looking to the future, this will hopefully be changed in 3.0, see this topic for details.

Update: regarding 3.0 change, see this issue. If you want this behavior to change please add +1 or comment. Right now it isn't clear if either approach is better as there are users that need the old behavior (see the issue for more details).