From 13b59b93565c48b33267684e112752aa1065ef7f Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 5 Jan 2025 22:01:15 +0900 Subject: [PATCH 1/3] Fixed to respect the nullValueProvider set on the property fixes #876 --- .../jackson/module/kotlin/KotlinValueInstantiator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt index 54e4cce3..8c3f2529 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt @@ -79,8 +79,8 @@ internal class KotlinValueInstantiator( paramType.isMarkedNullable -> null // Primitive types always try to get from a buffer, considering several settings jsonProp.type.isPrimitive -> buffer.getParameter(jsonProp) - // to get suitable "missing" value provided by deserializer - else -> valueDeserializer?.getAbsentValue(ctxt) + // to get suitable "missing" value provided by nullValueProvider + else -> jsonProp.nullValueProvider?.getAbsentValue(ctxt) } } From 703e032aa687a2941ceaa23355fc4db52de5747c Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 5 Jan 2025 22:12:50 +0900 Subject: [PATCH 2/3] Add tests --- .../module/kotlin/test/github/GitHub876.kt | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/GitHub876.kt diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/GitHub876.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/GitHub876.kt new file mode 100644 index 00000000..de1a21e3 --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/GitHub876.kt @@ -0,0 +1,152 @@ +package com.fasterxml.jackson.module.kotlin.test.github + +import com.fasterxml.jackson.annotation.JsonSetter +import com.fasterxml.jackson.annotation.Nulls +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class GitHub876 { + data class WithAnnotationWithoutDefault( + @JsonSetter(nulls = Nulls.AS_EMPTY) + val list: List, + @JsonSetter(nulls = Nulls.AS_EMPTY) + val map: Map, + @JsonSetter(nulls = Nulls.AS_EMPTY) + val string: String + ) + + @Nested + inner class WithAnnotationWithoutDefaultTest { + val mapper = jacksonObjectMapper() + + @Test + fun nullInput() { + val input = """{"list": null, "map": null, "string": null}""" + val expected = WithAnnotationWithoutDefault(emptyList(), emptyMap(), "") + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + + @Test + fun undefinedInput() { + val input = """{}""" + val expected = WithAnnotationWithoutDefault(emptyList(), emptyMap(), "") + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + } + + data class WithAnnotationWithDefault( + @JsonSetter(nulls = Nulls.AS_EMPTY) + val list: List = listOf("default"), + @JsonSetter(nulls = Nulls.AS_EMPTY) + val map: Map = mapOf("default" to "default"), + @JsonSetter(nulls = Nulls.AS_EMPTY) + val string: String = "default" + ) + + @Nested + inner class WithAnnotationWithDefaultTest { + val mapper = jacksonObjectMapper() + + @Test + fun nullInput() { + // If null is explicitly specified, the default value is not used + val input = """{"list": null, "map": null, "string": null}""" + val expected = WithAnnotationWithDefault(emptyList(), emptyMap(), "") + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + + @Test + fun undefinedInput() { + // If the input is undefined, the default value is used + val input = """{}""" + val expected = WithAnnotationWithDefault() + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + } + + // If it is set by configOverride, it is treated in the same way as if it were set by annotation + data class WithoutAnnotationWithoutDefault( + val list: List, + val map: Map, + val string: String + ) + + @Nested + inner class WithoutAnnotationWithoutDefaultTest { + val mapper = jacksonObjectMapper().apply { + configOverride(List::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) + configOverride(Map::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) + configOverride(String::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) + } + + @Test + fun nullInput() { + val input = """{"list": null, "map": null, "string": null}""" + val expected = WithoutAnnotationWithoutDefault(emptyList(), emptyMap(), "") + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + + @Test + fun undefinedInput() { + val input = """{}""" + val expected = WithoutAnnotationWithoutDefault(emptyList(), emptyMap(), "") + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + } + + data class WithoutAnnotationWithDefault( + val list: List = listOf("default"), + val map: Map = mapOf("default" to "default"), + val string: String = "default" + ) + + @Nested + inner class WithoutAnnotationWithDefaultTest { + val mapper = jacksonObjectMapper().apply { + configOverride(List::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) + configOverride(Map::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) + configOverride(String::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) + } + + @Test + fun nullInput() { + val input = """{"list": null, "map": null, "string": null}""" + val expected = WithoutAnnotationWithDefault(emptyList(), emptyMap(), "") + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + + @Test + fun undefinedInput() { + val input = """{}""" + val expected = WithoutAnnotationWithDefault() + + val actual = mapper.readValue(input) + + assertEquals(expected, actual) + } + } +} From 4c0a0678b42e3171a86bb8bce88d9645f238603d Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sun, 5 Jan 2025 22:44:17 +0900 Subject: [PATCH 3/3] Update release notes wrt #878 --- release-notes/CREDITS-2.x | 1 + release-notes/VERSION-2.x | 1 + 2 files changed, 2 insertions(+) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index c7b7f3b4..d9646d77 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -18,6 +18,7 @@ Contributors: # 2.19.0 (not yet released) WrongWrong (@k163377) +* #878: Fix for #876 * #868: Added test case for FAIL_ON_NULL_FOR_PRIMITIVES * #866: Upgrade to JUnit5 * #861: Update Kotlin to 1.9.24 diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index b7bab23f..f9e6e0ce 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -18,6 +18,7 @@ Co-maintainers: 2.19.0 (not yet released) +#878: Fixed a problem where settings like `@JsonSetter(nulls = AS_EMPTY)` were not being applied when the input was `undefined`. #869: By using Enum.entries in the acquisition of KotlinFeature.defaults, the initialization load was reduced, albeit slightly. #861: Kotlin has been upgraded to 1.9.24. #858: Minor performance improvement of findDefaultCreator in edge cases.