FasterXML / FasterXML/jackson-module-kotlin
Deserialization fails for class with single private accessor property
- Dominant language
- Kotlin
- Stars
- 1.2k
- Forks
- 187
- Avg merge
- 9h 48m
- Merged PRs (30d)
- 16
Description
### Search before asking
- [X] I searched in the [issues](https://github.com/FasterXML/jackson-module-kotlin/issues) and found nothing similar.
### Describe the bug
Deserialization fails for class with single private accessor property.
When deserializing, a MismatchedInputException occurs.
### To Reproduce
```kotlin
class A(
private val name: String,
)
class B(
private val name: String,
private val age: Int,
)
private val mapper: ObjectMapper = jacksonObjectMapper()
@Test
fun fail() {
// given
val json = """
{"name" : "hello"}
""".trimIndent()
// when, then
shouldThrow {
mapper.readValue(json, A::class.java)
}
}
@Test
fun success() {
// given
val json = """
{"name" : "hello", "age": 12}
""".trimIndent()
shouldNotThrowAny {
mapper.readValue(json, B::class.java)
}
}
```
### Expected behavior
A class with single private accessor property is properly deserialized.
**Actual behavior**
~~~
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `test.SimpleTest$A` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"name" : "hello"}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
~~~
### Versions
Kotlin: 1.8.0
Jackson-module-kotlin: 2.12.7
Jackson-databind: 2.12.7.1
### Additional context
I tried to dig into this and discovered the flow below:
(Code has been simplified)
1. In the "BasicDeserializerFactory _addExplicitAnyCreator" method, it is processed according to the number of constructor parameters.
~~~java
protected void _addExplicitAnyCreator(DeserializationContext ctxt,
BeanDescription beanDesc, CreatorCollector creators,
CreatorCandidate candidate, ConstructorDetector ctorDetector)
throws JsonMappingException
{
// Looks like there's bit of magic regarding 1-parameter creators; others simpler:
if (1 != candidate.paramCount()) {
}
}
~~~
2. Because of the private accessor, the useProps variable is set to false, so _creators[C_PROPS] of CreatorCollector cannot be set.
~~~java
default:
{ // Note: behavior pre-Jackson-2.12
final BeanPropertyDefinition paramDef = candidate.propertyDef(0);
// with heuristic, need to start with just explicit name
paramName = candidate.explicitParamName(0);
// If there's injection or explicit name, should be properties-based
useProps = (paramName != null) || (injectId != null);
if (!useProps && (paramDef != null)) {
// One more thing: if implicit name matches property with a getter
// or field, we'll consider it property-based as well
// 25-May-2018, tatu: as per [databind#2051], looks like we have to get
// not implicit name, but name with possible strategy-based-rename
// paramName = candidate.findImplicitParamName(0);
paramName = candidate.paramName(0);
useProps = (paramName != null) && paramDef.couldSerialize();
}
}
if (useProps) {
SettableBeanProperty[] properties = new SettableBeanProperty[] {
constructCreatorProperty(ctxt, beanDesc, paramName, 0, param, injectId)
};
creators.addPropertyCreator(candidate.creator(), true, properties);
return;
}
~~~
3. The MismatchedInputException occurs because "_propertyBasedCreator" is null in the "BeanDeserializer.deserializeFromObjectUsingNonDefault" method
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.