Skip to content

Commit 910ee1c

Browse files
committed
Merge remote-tracking branch 'FasterXML/2.x' into 3.x
2 parents d68a31b + 80b8371 commit 910ee1c

File tree

10 files changed

+129
-24
lines changed

10 files changed

+129
-24
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+
* #1025: Deprecate MissingKotlinParameterException and replace with new exception
2122
* #1020: Fixed old StrictNullChecks to throw exceptions similar to those thrown by new StrictNullChecks
2223
* #1018: Use MethodHandle in processing related to value class
2324
* #969: Cleanup of deprecated contents

release-notes/VERSION-2.x

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

1919
2.20.0 (not yet released)
20+
#1025: When a null is entered for a non-null parameter, the KotlinInvalidNullException is now thrown instead of the
21+
deprecated MissingKotlinParameterException.
22+
The new exception is a subclass of InvalidNullException.
23+
See the comment below for information contained in this exception.
24+
https://github.com/FasterXML/jackson-module-kotlin/issues/617#issuecomment-3124423585
2025
#1020: Exceptions thrown by the old StrictNullChecks are now the similar to the new StrictNullChecks.
2126
This means that the old StrictNullChecks will no longer throw MissingKotlinParameterException.
2227
See PR for what is thrown and how error messages change.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package tools.jackson.module.kotlin;
2+
3+
import tools.jackson.core.JsonParser;
4+
import tools.jackson.databind.DatabindException;
5+
import tools.jackson.databind.PropertyName;
6+
import tools.jackson.databind.exc.InvalidNullException;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
9+
10+
// Due to a limitation in KT-6653, there is no user-friendly way to override Java getters in Kotlin.
11+
// The reason for not having detailed information(e.g. KParameter) is to keep the class Serializable.
12+
/**
13+
* Specialized {@link DatabindException} sub-class used to indicate that a mandatory Kotlin creator parameter was
14+
* missing or null.
15+
*/
16+
public final class KotlinInvalidNullException extends InvalidNullException {
17+
@NotNull
18+
private final String kotlinPropertyName;
19+
20+
KotlinInvalidNullException(
21+
@Nullable
22+
String kotlinParameterName,
23+
@NotNull
24+
Class<?> valueClass,
25+
@NotNull
26+
JsonParser p,
27+
@NotNull
28+
String msg,
29+
@NotNull
30+
PropertyName pname
31+
) {
32+
super(p, msg, pname);
33+
// Basically, this will never be null, but it is handled here to avoid errors in unusual cases.
34+
this.kotlinPropertyName = kotlinParameterName == null ? "UNKNOWN" : kotlinParameterName;
35+
this._targetType = valueClass;
36+
}
37+
38+
/**
39+
* @return Parameter name in Kotlin.
40+
*/
41+
@NotNull
42+
public String getKotlinPropertyName() {
43+
return kotlinPropertyName;
44+
}
45+
46+
// region: Override getters to make nullability explicit and to explain its role in this class.
47+
/**
48+
* @return Parameter name in Jackson.
49+
*/
50+
@NotNull
51+
@Override
52+
public PropertyName getPropertyName() {
53+
return super.getPropertyName();
54+
}
55+
56+
/**
57+
* @return The {@link Class} object representing the class that declares the creator.
58+
*/
59+
@NotNull
60+
@Override
61+
public Class<?> getTargetType() {
62+
return super.getTargetType();
63+
}
64+
// endregion
65+
}

src/main/kotlin/tools/jackson/module/kotlin/Exceptions.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,10 @@ import kotlin.reflect.KParameter
1010
* parameter was missing or null.
1111
*/
1212
@Deprecated(
13-
"It is recommended that InvalidNullException be referenced when possible," +
14-
" as the change is discussed for 2.20 and later." +
15-
" See #617 for details.",
16-
ReplaceWith(
17-
"InvalidNullException",
18-
"com.fasterxml.jackson.databind.exc.InvalidNullException"
19-
),
20-
DeprecationLevel.WARNING
13+
"Since 2.20, this exception is no longer thrown and has been replaced by KotlinInvalidNullException. " +
14+
"See #617 for details.",
15+
ReplaceWith("KotlinInvalidNullException"),
16+
DeprecationLevel.ERROR
2117
)
2218
// When deserialized by the JDK, the parameter property will be null, ignoring nullability.
2319
// This is a temporary workaround for #572 and we will eventually remove this class.

src/main/kotlin/tools/jackson/module/kotlin/KotlinValueInstantiator.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,18 @@ internal class KotlinValueInstantiator(
9090
if (propType.requireEmptyValue()) {
9191
paramVal = valueDeserializer!!.getEmptyValue(ctxt)
9292
} else {
93+
val pname = jsonProp.name
9394
val isMissingAndRequired = isMissing && jsonProp.isRequired
9495

9596
// Since #310 reported that the calculation cost is high, isGenericTypeVar is determined last.
9697
if (isMissingAndRequired || (!paramType.isMarkedNullable && !paramType.isGenericTypeVar())) {
97-
throw MissingKotlinParameterException(
98-
parameter = paramDef,
99-
processor = ctxt.parser,
100-
msg = "Instantiation of ${this.valueTypeDesc} value failed for JSON property ${jsonProp.name} due to missing (therefore NULL) value for creator parameter ${paramDef.name} which is a non-nullable type"
101-
).wrapWithPath(this.valueClass, jsonProp.name)
98+
throw KotlinInvalidNullException(
99+
paramDef.name,
100+
this.valueClass,
101+
ctxt.parser,
102+
"Instantiation of ${this.valueTypeDesc} value failed for JSON property $pname due to missing (therefore NULL) value for creator parameter ${paramDef.name} which is a non-nullable type",
103+
jsonProp.fullName,
104+
).wrapWithPath(this.valueClass, pname)
102105
}
103106
}
104107
} else if (strictNullChecks) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package tools.jackson.module.kotlin
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import org.junit.jupiter.api.Test
5+
import org.junit.jupiter.api.assertThrows
6+
import kotlin.test.assertEquals
7+
8+
private data class Dto(
9+
val foo: String,
10+
@JsonProperty("bar")
11+
val _bar: String
12+
)
13+
14+
class KotlinInvalidNullExceptionTest {
15+
@Test
16+
fun fooTest() {
17+
val json = """{"bar":"bar"}"""
18+
val ex = assertThrows<KotlinInvalidNullException> { defaultMapper.readValue<Dto>(json) }
19+
20+
assertEquals("foo", ex.kotlinPropertyName)
21+
assertEquals("foo", ex.propertyName.simpleName)
22+
assertEquals(Dto::class, ex.targetType.kotlin)
23+
}
24+
25+
@Test
26+
fun barTest() {
27+
val json = """{"foo":"foo","bar":null}"""
28+
val ex = assertThrows<KotlinInvalidNullException> { defaultMapper.readValue<Dto>(json) }
29+
30+
assertEquals("_bar", ex.kotlinPropertyName)
31+
assertEquals("bar", ex.propertyName.simpleName)
32+
assertEquals(Dto::class, ex.targetType.kotlin)
33+
}
34+
}

src/test/kotlin/tools/jackson/module/kotlin/MissingKotlinParameterExceptionTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import kotlin.test.assertNotNull
55
import kotlin.test.assertNull
66

77
class MissingKotlinParameterExceptionTest {
8+
@Suppress("DEPRECATION_ERROR")
89
@Test
910
fun jdkSerializabilityTest() {
1011
val param = ::MissingKotlinParameterException.parameters.first()

src/test/kotlin/tools/jackson/module/kotlin/test/NullToDefaultTests.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import org.junit.jupiter.api.assertThrows
77
import tools.jackson.databind.DeserializationFeature
88
import tools.jackson.databind.json.JsonMapper
99
import tools.jackson.module.kotlin.KotlinFeature.NullIsSameAsDefault
10-
import tools.jackson.module.kotlin.MissingKotlinParameterException
10+
import tools.jackson.module.kotlin.KotlinInvalidNullException
1111
import tools.jackson.module.kotlin.kotlinModule
1212
import tools.jackson.module.kotlin.readValue
1313

@@ -153,7 +153,7 @@ class TestNullToDefault {
153153

154154
@Test
155155
fun shouldThrowExceptionWhenProvidedNullForNotNullFieldWithoutDefault() {
156-
assertThrows<MissingKotlinParameterException> {
156+
assertThrows<KotlinInvalidNullException> {
157157
createMapper(true).readValue<TestClass>(
158158
"""{
159159
"text": null

src/test/kotlin/tools/jackson/module/kotlin/test/github/Github168.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
44
import org.junit.jupiter.api.Test
55
import org.junit.jupiter.api.assertThrows
66
import tools.jackson.databind.ObjectMapper
7-
import tools.jackson.module.kotlin.MissingKotlinParameterException
7+
import tools.jackson.module.kotlin.KotlinInvalidNullException
88
import tools.jackson.module.kotlin.defaultMapper
99
import tools.jackson.module.kotlin.jacksonObjectMapper
1010
import tools.jackson.module.kotlin.readValue
@@ -24,7 +24,7 @@ class TestGithub168 {
2424

2525
@Test
2626
fun testIfRequiredIsReallyRequiredWhenAbsent() {
27-
assertThrows<MissingKotlinParameterException> {
27+
assertThrows<KotlinInvalidNullException> {
2828
val obj = defaultMapper.readValue<TestClass>("""{"baz":"whatever"}""")
2929
assertEquals("whatever", obj.baz)
3030
}

src/test/kotlin/tools/jackson/module/kotlin/test/github/Github32.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
44
import org.junit.jupiter.api.Test
55
import org.junit.jupiter.api.assertThrows
66
import tools.jackson.databind.DatabindException
7-
import tools.jackson.databind.exc.MismatchedInputException
7+
import tools.jackson.module.kotlin.KotlinInvalidNullException
88
import tools.jackson.module.kotlin.defaultMapper
99
import tools.jackson.module.kotlin.readValue
1010

@@ -19,8 +19,8 @@ private class TestGithub32 {
1919
}
2020

2121
@Test fun `missing mandatory data class constructor param`() {
22-
val thrown = assertThrows<MismatchedInputException>(
23-
"MissingKotlinParameterException with missing `firstName` parameter"
22+
val thrown = assertThrows<KotlinInvalidNullException>(
23+
"KotlinInvalidNullException with missing `firstName` parameter"
2424
) {
2525
defaultMapper.readValue<Person>("""
2626
{
@@ -35,7 +35,7 @@ private class TestGithub32 {
3535
}
3636

3737
@Test fun `null mandatory data class constructor param`() {
38-
val thrown = assertThrows<MismatchedInputException> {
38+
val thrown = assertThrows<KotlinInvalidNullException> {
3939
defaultMapper.readValue<Person>("""
4040
{
4141
"firstName": null,
@@ -50,7 +50,7 @@ private class TestGithub32 {
5050
}
5151

5252
@Test fun `missing mandatory constructor param - nested in class with default constructor`() {
53-
val thrown = assertThrows<MismatchedInputException> {
53+
val thrown = assertThrows<KotlinInvalidNullException> {
5454
defaultMapper.readValue<WrapperWithDefaultContructor>("""
5555
{
5656
"person": {
@@ -66,7 +66,7 @@ private class TestGithub32 {
6666
}
6767

6868
@Test fun `missing mandatory constructor param - nested in class with single arg constructor`() {
69-
val thrown = assertThrows<MismatchedInputException> {
69+
val thrown = assertThrows<KotlinInvalidNullException> {
7070
defaultMapper.readValue<WrapperWithArgsContructor>("""
7171
{
7272
"person": {
@@ -82,7 +82,7 @@ private class TestGithub32 {
8282
}
8383

8484
@Test fun `missing mandatory constructor param - nested in class with List arg constructor`() {
85-
val thrown = assertThrows<MismatchedInputException> {
85+
val thrown = assertThrows<KotlinInvalidNullException> {
8686
defaultMapper.readValue<Crowd>("""
8787
{
8888
"people": [

0 commit comments

Comments
 (0)