Skip to content

Fix for #876 #878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions release-notes/CREDITS-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<String>,
@JsonSetter(nulls = Nulls.AS_EMPTY)
val map: Map<String, String>,
@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<WithAnnotationWithoutDefault>(input)

assertEquals(expected, actual)
}

@Test
fun undefinedInput() {
val input = """{}"""
val expected = WithAnnotationWithoutDefault(emptyList(), emptyMap(), "")

val actual = mapper.readValue<WithAnnotationWithoutDefault>(input)

assertEquals(expected, actual)
}
}

data class WithAnnotationWithDefault(
@JsonSetter(nulls = Nulls.AS_EMPTY)
val list: List<String> = listOf("default"),
@JsonSetter(nulls = Nulls.AS_EMPTY)
val map: Map<String, String> = 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<WithAnnotationWithDefault>(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<WithAnnotationWithDefault>(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<String>,
val map: Map<String, String>,
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<WithoutAnnotationWithoutDefault>(input)

assertEquals(expected, actual)
}

@Test
fun undefinedInput() {
val input = """{}"""
val expected = WithoutAnnotationWithoutDefault(emptyList(), emptyMap(), "")

val actual = mapper.readValue<WithoutAnnotationWithoutDefault>(input)

assertEquals(expected, actual)
}
}

data class WithoutAnnotationWithDefault(
val list: List<String> = listOf("default"),
val map: Map<String, String> = 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<WithoutAnnotationWithDefault>(input)

assertEquals(expected, actual)
}

@Test
fun undefinedInput() {
val input = """{}"""
val expected = WithoutAnnotationWithDefault()

val actual = mapper.readValue<WithoutAnnotationWithDefault>(input)

assertEquals(expected, actual)
}
}
}