FasterXML / FasterXML/jackson-module-kotlin

Deserialization fails for class with single private accessor property

Open
#753 7 comments 2 reactions 0 assignees View on GitHub
bug
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.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.