Skip to content

Commit 4bd4845

Browse files
authored
Merge pull request #687 from k163377/feat/refactor-valueinstantiator
Optimize and Refactor KotlinValueInstantiator.createFromObjectWith
2 parents ef6737d + 578ce84 commit 4bd4845

File tree

3 files changed

+47
-30
lines changed

3 files changed

+47
-30
lines changed

release-notes/CREDITS-2.x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Contributors:
1818
# 2.16.0 (not yet released)
1919

2020
WrongWrong (@k163377)
21+
* #687: Optimize and Refactor KotlinValueInstantiator.createFromObjectWith
2122
* #685: Streamline default value management for KotlinFeatures
2223
* #684: Update Kotlin Version to 1.6
2324
* #682: Remove MissingKotlinParameterException and replace with MismatchedInputException

release-notes/VERSION-2.x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Co-maintainers:
1818

1919
2.16.0 (not yet released)
2020

21+
#687: Optimize and Refactor KotlinValueInstantiator.createFromObjectWith.
2122
#685: Streamline default value management for KotlinFeatures.
2223
This improves the initialization cost of kotlin-module a little.
2324
#684: Kotlin 1.5 has been deprecated and the minimum supported Kotlin version will be updated to 1.6.

src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.fasterxml.jackson.module.kotlin
33
import com.fasterxml.jackson.databind.BeanDescription
44
import com.fasterxml.jackson.databind.DeserializationConfig
55
import com.fasterxml.jackson.databind.DeserializationContext
6+
import com.fasterxml.jackson.databind.JavaType
67
import com.fasterxml.jackson.databind.deser.SettableBeanProperty
78
import com.fasterxml.jackson.databind.deser.ValueInstantiator
89
import com.fasterxml.jackson.databind.deser.ValueInstantiators
@@ -13,6 +14,7 @@ import com.fasterxml.jackson.databind.exc.MismatchedInputException
1314
import java.lang.reflect.TypeVariable
1415
import kotlin.reflect.KParameter
1516
import kotlin.reflect.KType
17+
import kotlin.reflect.KTypeProjection
1618
import kotlin.reflect.jvm.javaType
1719

1820
internal class KotlinValueInstantiator(
@@ -23,6 +25,13 @@ internal class KotlinValueInstantiator(
2325
private val nullIsSameAsDefault: Boolean,
2426
private val strictNullChecks: Boolean
2527
) : StdValueInstantiator(src) {
28+
private fun JavaType.requireEmptyValue() =
29+
(nullToEmptyCollection && this.isCollectionLikeType) || (nullToEmptyMap && this.isMapLikeType)
30+
31+
private fun KType.isGenericTypeVar() = javaType is TypeVariable<*>
32+
33+
private fun List<KTypeProjection>.markedNonNullAt(index: Int) = getOrNull(index)?.type?.isMarkedNullable == false
34+
2635
override fun createFromObjectWith(
2736
ctxt: DeserializationContext,
2837
props: Array<out SettableBeanProperty>,
@@ -58,14 +67,15 @@ internal class KotlinValueInstantiator(
5867
return@forEachIndexed
5968
}
6069

70+
val paramType = paramDef.type
6171
var paramVal = if (!isMissing || paramDef.isPrimitive() || jsonProp.hasInjectableValueId()) {
6272
val tempParamVal = buffer.getParameter(jsonProp)
6373
if (nullIsSameAsDefault && tempParamVal == null && paramDef.isOptional) {
6474
return@forEachIndexed
6575
}
6676
tempParamVal
6777
} else {
68-
if(paramDef.type.isMarkedNullable) {
78+
if(paramType.isMarkedNullable) {
6979
// do not try to create any object if it is nullable and the value is missing
7080
null
7181
} else {
@@ -74,46 +84,51 @@ internal class KotlinValueInstantiator(
7484
}
7585
}
7686

77-
if (paramVal == null && ((nullToEmptyCollection && jsonProp.type.isCollectionLikeType) || (nullToEmptyMap && jsonProp.type.isMapLikeType))) {
78-
paramVal = NullsAsEmptyProvider(jsonProp.valueDeserializer).getNullValue(ctxt)
79-
}
87+
val propType = jsonProp.type
8088

81-
val isGenericTypeVar = paramDef.type.javaType is TypeVariable<*>
82-
val isMissingAndRequired = paramVal == null && isMissing && jsonProp.isRequired
83-
if (isMissingAndRequired ||
84-
(!isGenericTypeVar && paramVal == null && !paramDef.type.isMarkedNullable)) {
85-
throw MismatchedInputException.from(
86-
ctxt.parser,
87-
jsonProp.type,
88-
"Instantiation of $valueTypeDesc value failed for JSON property ${jsonProp.name} " +
89-
"due to missing (therefore NULL) value for creator parameter ${paramDef.name} " +
90-
"which is a non-nullable type"
91-
).wrapWithPath(this.valueClass, jsonProp.name)
92-
}
89+
if (paramVal == null) {
90+
if (propType.requireEmptyValue()) {
91+
paramVal = NullsAsEmptyProvider(jsonProp.valueDeserializer).getNullValue(ctxt)
92+
} else {
93+
val isMissingAndRequired = isMissing && jsonProp.isRequired
94+
95+
// Since #310 reported that the calculation cost is high, isGenericTypeVar is determined last.
96+
if (isMissingAndRequired || (!paramType.isMarkedNullable && !paramType.isGenericTypeVar())) {
97+
throw MismatchedInputException.from(
98+
ctxt.parser,
99+
propType,
100+
"Instantiation of $valueTypeDesc value failed for JSON property ${jsonProp.name} " +
101+
"due to missing (therefore NULL) value for creator parameter ${paramDef.name} " +
102+
"which is a non-nullable type"
103+
).wrapWithPath(this.valueClass, jsonProp.name)
104+
}
105+
}
106+
} else if (strictNullChecks) {
107+
val arguments = paramType.arguments
93108

94-
if (strictNullChecks && paramVal != null) {
95-
var paramType: String? = null
109+
var paramTypeStr: String? = null
96110
var itemType: KType? = null
97-
if (jsonProp.type.isCollectionLikeType && paramDef.type.arguments.getOrNull(0)?.type?.isMarkedNullable == false && (paramVal as Collection<*>).any { it == null }) {
98-
paramType = "collection"
99-
itemType = paramDef.type.arguments[0].type
111+
112+
if (propType.isCollectionLikeType && arguments.markedNonNullAt(0) && (paramVal as Collection<*>).any { it == null }) {
113+
paramTypeStr = "collection"
114+
itemType = arguments[0].type
100115
}
101116

102-
if (jsonProp.type.isMapLikeType && paramDef.type.arguments.getOrNull(1)?.type?.isMarkedNullable == false && (paramVal as Map<*, *>).any { it.value == null }) {
103-
paramType = "map"
104-
itemType = paramDef.type.arguments[1].type
117+
if (propType.isMapLikeType && arguments.markedNonNullAt(1) && (paramVal as Map<*, *>).any { it.value == null }) {
118+
paramTypeStr = "map"
119+
itemType = arguments[1].type
105120
}
106121

107-
if (jsonProp.type.isArrayType && paramDef.type.arguments.getOrNull(0)?.type?.isMarkedNullable == false && (paramVal as Array<*>).any { it == null }) {
108-
paramType = "array"
109-
itemType = paramDef.type.arguments[0].type
122+
if (propType.isArrayType && arguments.markedNonNullAt(0) && (paramVal as Array<*>).any { it == null }) {
123+
paramTypeStr = "array"
124+
itemType = arguments[0].type
110125
}
111126

112-
if (paramType != null && itemType != null) {
127+
if (paramTypeStr != null && itemType != null) {
113128
throw MismatchedInputException.from(
114129
ctxt.parser,
115-
jsonProp.type,
116-
"Instantiation of $itemType $paramType failed for JSON property ${jsonProp.name} due to null value in a $paramType that does not allow null values"
130+
propType,
131+
"Instantiation of $itemType $paramTypeStr failed for JSON property ${jsonProp.name} due to null value in a $paramTypeStr that does not allow null values"
117132
).wrapWithPath(this.valueClass, jsonProp.name)
118133
}
119134
}

0 commit comments

Comments
 (0)