Skip to content

Commit d8b681b

Browse files
authored
Merge pull request #1020 from k163377/mkpe
Fixed old `StrictNullChecks` to throw exceptions similar to those thrown by new `StrictNullChecks`
2 parents d7f228e + 14cc112 commit d8b681b

File tree

4 files changed

+36
-30
lines changed

4 files changed

+36
-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.20.0 (not yet released)
1919

2020
WrongWrong (@k163377)
21+
* #1020: Fixed old StrictNullChecks to throw exceptions similar to those thrown by new StrictNullChecks
2122
* #1018: Use MethodHandle in processing related to value class
2223
* #969: Cleanup of deprecated contents
2324
* #967: Update settings for 2.20

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ Co-maintainers:
1717
------------------------------------------------------------------------
1818

1919
2.20.0 (not yet released)
20+
#1020: Exceptions thrown by the old StrictNullChecks are now the similar to the new StrictNullChecks.
21+
This means that the old StrictNullChecks will no longer throw MissingKotlinParameterException.
22+
See PR for what is thrown and how error messages change.
2023
#1018: Improved handling of `value class` has improved performance for both serialization and deserialization.
2124
In particular, for serialization, proper caching has improved throughput by a factor of 2 or more in the general cases.
2225
Also, replacing function execution by reflection with `MethodHandle` improved throughput by several percent for both serialization and deserialization.

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

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.deser.ValueInstantiator
1111
import com.fasterxml.jackson.databind.deser.ValueInstantiators
1212
import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer
1313
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator
14+
import com.fasterxml.jackson.databind.exc.InvalidNullException
1415
import java.lang.reflect.TypeVariable
1516
import kotlin.reflect.KType
1617
import kotlin.reflect.KTypeProjection
@@ -103,31 +104,32 @@ internal class KotlinValueInstantiator(
103104
} else if (strictNullChecks) {
104105
val arguments = paramType.arguments
105106

106-
var paramTypeStr: String? = null
107-
var itemType: KType? = null
108-
109-
if (propType.isCollectionLikeType && arguments.markedNonNullAt(0) && (paramVal as Collection<*>).any { it == null }) {
110-
paramTypeStr = "collection"
111-
itemType = arguments[0].type
112-
}
113-
114-
if (propType.isMapLikeType && arguments.markedNonNullAt(1) && (paramVal as Map<*, *>).any { it.value == null }) {
115-
paramTypeStr = "map"
116-
itemType = arguments[1].type
117-
}
118-
119-
if (propType.isArrayType && arguments.markedNonNullAt(0) && (paramVal as Array<*>).any { it == null }) {
120-
paramTypeStr = "array"
121-
itemType = arguments[0].type
107+
// To make the behavior the same as deserialization of each element using NullsFailProvider,
108+
// first wrapWithPath with paramVal and key.
109+
val ex = when {
110+
propType.isCollectionLikeType && arguments.markedNonNullAt(0) -> {
111+
(paramVal as Collection<*>).indexOf(null).takeIf { it >= 0 }?.let {
112+
InvalidNullException.from(ctxt, jsonProp.fullName, jsonProp.type)
113+
.wrapWithPath(paramVal, it)
114+
}
115+
}
116+
propType.isMapLikeType && arguments.markedNonNullAt(1) -> {
117+
(paramVal as Map<*, *>).entries.find { (_, v) -> v == null }?.let { (k, _) ->
118+
InvalidNullException.from(ctxt, jsonProp.fullName, jsonProp.type)
119+
.wrapWithPath(paramVal, k.toString())
120+
}
121+
}
122+
propType.isArrayType && arguments.markedNonNullAt(0) -> {
123+
(paramVal as Array<*>).indexOf(null).takeIf { it >= 0 }?.let {
124+
InvalidNullException.from(ctxt, jsonProp.fullName, jsonProp.type)
125+
.wrapWithPath(paramVal, it)
126+
}
127+
}
128+
else -> null
122129
}
123130

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

133135
bucket[paramDef] = paramVal

src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/StrictNullChecksTestOld.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.fasterxml.jackson.module.kotlin.test
22

3+
import com.fasterxml.jackson.databind.exc.InvalidNullException
34
import com.fasterxml.jackson.module.kotlin.KotlinFeature
4-
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
55
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
66
import com.fasterxml.jackson.module.kotlin.readValue
77
import org.junit.jupiter.api.Assertions.assertArrayEquals
@@ -32,7 +32,7 @@ class StrictNullChecksTestOld {
3232

3333
@Test
3434
fun testListOfInt() {
35-
assertThrows<MissingKotlinParameterException> {
35+
assertThrows<InvalidNullException> {
3636
val json = """{"samples":[1, null]}"""
3737
mapper.readValue<ClassWithListOfInt>(json)
3838
}
@@ -62,7 +62,7 @@ class StrictNullChecksTestOld {
6262

6363
@Test
6464
fun testArrayOfInt() {
65-
assertThrows<MissingKotlinParameterException> {
65+
assertThrows<InvalidNullException> {
6666
val json = """{"samples":[1, null]}"""
6767
mapper.readValue<ClassWithArrayOfInt>(json)
6868
}
@@ -92,7 +92,7 @@ class StrictNullChecksTestOld {
9292

9393
@Test
9494
fun testMapOfStringToIntWithNullValue() {
95-
assertThrows<MissingKotlinParameterException> {
95+
assertThrows<InvalidNullException> {
9696
val json = """{ "samples": { "key": null } }"""
9797
mapper.readValue<ClassWithMapOfStringToInt>(json)
9898
}
@@ -121,7 +121,7 @@ class StrictNullChecksTestOld {
121121
@Disabled // this is a hard problem to solve and is currently not addressed
122122
@Test
123123
fun testListOfGenericWithNullValue() {
124-
assertThrows<MissingKotlinParameterException> {
124+
assertThrows<InvalidNullException> {
125125
val json = """{"samples":[1, null]}"""
126126
mapper.readValue<TestClass<List<Int>>>(json)
127127
}
@@ -137,7 +137,7 @@ class StrictNullChecksTestOld {
137137
@Disabled // this is a hard problem to solve and is currently not addressed
138138
@Test
139139
fun testMapOfGenericWithNullValue() {
140-
assertThrows<MissingKotlinParameterException> {
140+
assertThrows<InvalidNullException> {
141141
val json = """{ "samples": { "key": null } }"""
142142
mapper.readValue<TestClass<Map<String, Int>>>(json)
143143
}
@@ -153,7 +153,7 @@ class StrictNullChecksTestOld {
153153
@Disabled // this is a hard problem to solve and is currently not addressed
154154
@Test
155155
fun testArrayOfGenericWithNullValue() {
156-
assertThrows<MissingKotlinParameterException> {
156+
assertThrows<InvalidNullException> {
157157
val json = """{"samples":[1, null]}"""
158158
mapper.readValue<TestClass<Array<Int>>>(json)
159159
}

0 commit comments

Comments
 (0)