@@ -3,6 +3,7 @@ package com.fasterxml.jackson.module.kotlin
33import com.fasterxml.jackson.databind.BeanDescription
44import com.fasterxml.jackson.databind.DeserializationConfig
55import com.fasterxml.jackson.databind.DeserializationContext
6+ import com.fasterxml.jackson.databind.JavaType
67import com.fasterxml.jackson.databind.deser.SettableBeanProperty
78import com.fasterxml.jackson.databind.deser.ValueInstantiator
89import com.fasterxml.jackson.databind.deser.ValueInstantiators
@@ -13,6 +14,7 @@ import com.fasterxml.jackson.databind.exc.MismatchedInputException
1314import java.lang.reflect.TypeVariable
1415import kotlin.reflect.KParameter
1516import kotlin.reflect.KType
17+ import kotlin.reflect.KTypeProjection
1618import kotlin.reflect.jvm.javaType
1719
1820internal 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