Unannotated single-argument constructor / factory method not considered a creator
See original GitHub issueTake the following domain class on Java 8 compiled with -parameters
:
class SingleValueWrapper {
private final Integer integer;
public SingleOtherValue(Integer integer) {
this.integer = integer;
}
}
This test:
@Test
public void testname() throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ParameterNamesModule());
SingleValueWrapper value = mapper.readValue("{ \"integer\" : 2 }", SingleValueWrapper.class);
assertThat(value, is(notNullValue()));
}
fails with:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.example.LombokImmutablesTests$SingleValueWrapper` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{ "integer" : 2 }"; line: 1, column: 3]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1031)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1290)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
at com.example.LombokImmutablesTests.testname4(LombokImmutablesTests.java:73)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
A couple of more observations:
- Adding an explicit annotation (e.g.
@ConstructorProperties
) makes the test go green. Unfortunately I don’t control the class so that I cannot add any annotations. Also, I thought adding the parameter names module should be sufficient as a name can now be derived from the class and additional annotations shouldn’t be needed. I.e. the annotation would just redeclare what’s already defined. - Adding an additional property makes the test pass. This is probably what strikes me most about the problem as it’s complete unintuitive why a constructor wouldn’t work for one parameter, but would for more than one.
- The same applies if you rather expose a factory method than a constructor.
- Even configuring a
Mode
explicitly doesn’t change anything about the failing test. - The execution at some point invokes
findNameForDeserialization(…)
which ´ParameterNamesAnnotationIntrospector` does not override. Is that by design? Because if I go ahead and implement that method like this:
@Override
public PropertyName findNameForDeserialization(Annotated a) {
if (a instanceof AnnotatedParameter) {
return PropertyName.construct(findParameterName((AnnotatedParameter) a));
}
return super.findNameForDeserialization(a);
}
things start to work again. I can see that this method is supposed to explicitly obtain a name from an annotation but it feels like that path is favored for a one-argument constructor.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:2
- Comments:26 (15 by maintainers)
Top Results From Across the Web
ObjectMapper can't deserialize without default constructor ...
Constructor /factory method where every argument is annotated with either JsonProperty or JacksonInject , to indicate name of property to bind to.
Read more >How does the default mode work for @JsonCreator?
in cases of single-argument creator (constructor or factory method, annotated with `@JsonCreator`, but without: 1. `mode` property to determine which type ...
Read more >Jackson 2.12 Most Wanted (3/5) - cowtowncoder - Medium
Consider this explicitly annotated POJO: ... Because 1-argument constructors (and static factory methods) are ambiguous: there are 2 ...
Read more >@NoArgsConstructor, @RequiredArgsConstructor, ...
Constructors made to order: Generates constructors that take no arguments, one argument per final / non-null field, or one argument for every field....
Read more >JsonCreator (Jackson-annotations 2.9.0 API) - FasterXML
Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@lpandzic Unless a very specific combo-fix (one part in jackson-databind, other [possibly] in java8 module), yes. So challenge would be that of restricting scope to – I think? – case of 1-argument, non-annotated constructor (and no other constructors).
Thinking about this aloud, addition of Builder-style construction in 2.10 and later would allow defining a new configuration setting, which would allow
Enum
for something likeCreatorDiscovery
or such…and that could change settings to, say:JsonCreator
, introspection [current setting]@JsonCreator
(but what about mode?)Mode.PROPERTIES
if not specified (no annotation or nomode
) – most likely option you would prefer?Mode.DELEGATING
if mode not specified, for 1-arg constructorand with that actually java8 module would not need anything. Alternatively, bit more complicated, would be to define something like
CreatorConfiguration
POJO, with accessors. But that might be more useful for case of trying to select from multiple constructors, something not yet supported.Now that I wrote this, it actually seems much more doable. WDYT? One thing I lack right now is time to work on things but I sort of like this approach.
FYI, my specific use case is lombok
@Value
classes. Which are classes with all-arg constructors and all fields final. For the cases where there are multiple fields everything works fine by default. However for the classes with a single field, this behaviour surprises me.However what about a setting
HANDLE_SINGLE_ARG_CONSTRUCTOR_AS_PROPERTY_CREATOR
? Is there any reason why adding such a setting would not be possible (or would not be a good idea)?