Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.deser.ValueInstantiator
import com.fasterxml.jackson.databind.deser.ValueInstantiators
import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator
import com.fasterxml.jackson.databind.exc.InvalidNullException
import java.lang.reflect.TypeVariable
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
Expand Down Expand Up @@ -103,31 +104,32 @@ internal class KotlinValueInstantiator(
} else if (strictNullChecks) {
val arguments = paramType.arguments

var paramTypeStr: String? = null
var itemType: KType? = null

if (propType.isCollectionLikeType && arguments.markedNonNullAt(0) && (paramVal as Collection<*>).any { it == null }) {
paramTypeStr = "collection"
itemType = arguments[0].type
}

if (propType.isMapLikeType && arguments.markedNonNullAt(1) && (paramVal as Map<*, *>).any { it.value == null }) {
paramTypeStr = "map"
itemType = arguments[1].type
}

if (propType.isArrayType && arguments.markedNonNullAt(0) && (paramVal as Array<*>).any { it == null }) {
paramTypeStr = "array"
itemType = arguments[0].type
// To make the behavior the same as deserialization of each element using NullsFailProvider,
// first wrapWithPath with paramVal and key.
val ex = when {
Copy link

Copilot AI Jul 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The when block that builds InvalidNullException contains repeated patterns for collection and array cases. Consider extracting a helper function to handle index-based null checks and exception wrapping to reduce duplication and improve readability.

Copilot uses AI. Check for mistakes.
propType.isCollectionLikeType && arguments.markedNonNullAt(0) -> {
(paramVal as Collection<*>).indexOf(null).takeIf { it >= 0 }?.let {
InvalidNullException.from(ctxt, jsonProp.fullName, jsonProp.type)
.wrapWithPath(paramVal, it)
}
}
propType.isMapLikeType && arguments.markedNonNullAt(1) -> {
(paramVal as Map<*, *>).entries.find { (_, v) -> v == null }?.let { (k, _) ->
InvalidNullException.from(ctxt, jsonProp.fullName, jsonProp.type)
.wrapWithPath(paramVal, k.toString())
}
}
propType.isArrayType && arguments.markedNonNullAt(0) -> {
(paramVal as Array<*>).indexOf(null).takeIf { it >= 0 }?.let {
InvalidNullException.from(ctxt, jsonProp.fullName, jsonProp.type)
.wrapWithPath(paramVal, it)
}
}
else -> null
}

if (paramTypeStr != null && itemType != null) {
throw MissingKotlinParameterException(
parameter = paramDef,
processor = ctxt.parser,
msg = "Instantiation of $itemType $paramType failed for JSON property ${jsonProp.name} due to null value in a $paramType that does not allow null values"
).wrapWithPath(this.valueClass, jsonProp.name)
}
// Then, wrapWithPath with this property.
ex?.let { throw it.wrapWithPath(this.valueClass, jsonProp.name) }
}

bucket[paramDef] = paramVal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.fasterxml.jackson.module.kotlin.test

import com.fasterxml.jackson.databind.exc.InvalidNullException
import com.fasterxml.jackson.module.kotlin.KotlinFeature
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.junit.jupiter.api.Assertions.assertArrayEquals
Expand Down Expand Up @@ -32,7 +32,7 @@ class StrictNullChecksTestOld {

@Test
fun testListOfInt() {
assertThrows<MissingKotlinParameterException> {
assertThrows<InvalidNullException> {
val json = """{"samples":[1, null]}"""
mapper.readValue<ClassWithListOfInt>(json)
}
Expand Down Expand Up @@ -62,7 +62,7 @@ class StrictNullChecksTestOld {

@Test
fun testArrayOfInt() {
assertThrows<MissingKotlinParameterException> {
assertThrows<InvalidNullException> {
val json = """{"samples":[1, null]}"""
mapper.readValue<ClassWithArrayOfInt>(json)
}
Expand Down Expand Up @@ -92,7 +92,7 @@ class StrictNullChecksTestOld {

@Test
fun testMapOfStringToIntWithNullValue() {
assertThrows<MissingKotlinParameterException> {
assertThrows<InvalidNullException> {
val json = """{ "samples": { "key": null } }"""
mapper.readValue<ClassWithMapOfStringToInt>(json)
}
Expand Down Expand Up @@ -121,7 +121,7 @@ class StrictNullChecksTestOld {
@Disabled // this is a hard problem to solve and is currently not addressed
@Test
fun testListOfGenericWithNullValue() {
assertThrows<MissingKotlinParameterException> {
assertThrows<InvalidNullException> {
val json = """{"samples":[1, null]}"""
mapper.readValue<TestClass<List<Int>>>(json)
}
Expand All @@ -137,7 +137,7 @@ class StrictNullChecksTestOld {
@Disabled // this is a hard problem to solve and is currently not addressed
@Test
fun testMapOfGenericWithNullValue() {
assertThrows<MissingKotlinParameterException> {
assertThrows<InvalidNullException> {
val json = """{ "samples": { "key": null } }"""
mapper.readValue<TestClass<Map<String, Int>>>(json)
}
Expand All @@ -153,7 +153,7 @@ class StrictNullChecksTestOld {
@Disabled // this is a hard problem to solve and is currently not addressed
@Test
fun testArrayOfGenericWithNullValue() {
assertThrows<MissingKotlinParameterException> {
assertThrows<InvalidNullException> {
val json = """{"samples":[1, null]}"""
mapper.readValue<TestClass<Array<Int>>>(json)
}
Expand Down