diff --git a/pom.xml b/pom.xml index 3844ea014..aa02b5b35 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ com.fasterxml.jackson jackson-base - 2.13.0-rc2-SNAPSHOT + 2.13.0-SNAPSHOT com.fasterxml.jackson.module jackson-module-kotlin diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index ff253804e..629e1f913 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -32,6 +32,10 @@ wrongwrong (k163377@github) * Contributed #438: Fixed mapping failure when `private` `companion object` is named (2.13) +Dmitri Domanine (novtor@github) +* Contributed fix for #490: Missing value of type JsonNode? is deserialized as NullNode instead of null + (2.13) + Marshall Pierce (marshallpierce@github) * #474: Reported disrespect for @JsonProperty on parent class (2.12.NEXT) 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 aed1ee847..2196d9687 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt @@ -101,8 +101,13 @@ internal class KotlinValueInstantiator( } tempParamVal } else { - // trying to get suitable "missing" value provided by deserializer - jsonProp.valueDeserializer?.getNullValue(ctxt) + if(paramDef.type.isMarkedNullable) { + // do not try to create any object if it is nullable and the value is missing + null + } else { + // to get suitable "missing" value provided by deserializer + jsonProp.valueDeserializer?.getAbsentValue(ctxt) + } } if (paramVal == null && ((nullToEmptyCollection && jsonProp.type.isCollectionLikeType) || (nullToEmptyMap && jsonProp.type.isMapLikeType))) { diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github490.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github490.kt new file mode 100644 index 000000000..38fe26109 --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github490.kt @@ -0,0 +1,81 @@ +package com.fasterxml.jackson.module.kotlin.test.github + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.node.NullNode +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import org.hamcrest.CoreMatchers +import org.hamcrest.MatcherAssert.assertThat +import org.junit.Test + +class TestGithub490 { + val mapper = jacksonObjectMapper() + val value: DataClassWithAllNullableParams = mapper.readValue( + "{" + + "\"jsonNodeValueWithNullAsDefaultProvidedNull\":null, " + + "\"jsonNodeValueProvidedNull\":null}" + ) + + @Test + fun testKotlinDeserialization_intValue() { + assertThat( + "Nullable missing Int value should be deserialized as null", + value.intValue, + CoreMatchers.nullValue() + ) + } + + @Test + fun testKotlinDeserialization_stringValue() { + assertThat( + "Nullable missing String value should be deserialized as null", + value.stringValue, + CoreMatchers.nullValue() + ) + } + + @Test + fun testKotlinDeserialization_jsonNodeValue() { + assertThat( + "Nullable missing JsonNode value should be deserialized as null and not as NullNode", + value.jsonNodeValue, + CoreMatchers.nullValue() + ) + } + + @Test + fun testKotlinDeserialization_jsonNodeValueProvidedNull() { + assertThat( + "Nullable JsonNode value provided as null should be deserialized as NullNode", + value.jsonNodeValueProvidedNull, + CoreMatchers.equalTo(NullNode.instance) + ) + } + + @Test + fun testKotlinDeserialization_jsonNodeValueWithNullAsDefault() { + assertThat( + "Nullable by default missing JsonNode value should be deserialized as null and not as NullNode", + value.jsonNodeValueWithNullAsDefault, + CoreMatchers.nullValue() + ) + } + + @Test + fun testKotlinDeserialization_jsonNodeValueWithNullAsDefaultProvidedNull() { + assertThat( + "Nullable by default JsonNode with provided null value in payload should be deserialized as NullNode", + value.jsonNodeValueWithNullAsDefaultProvidedNull, + CoreMatchers.equalTo(NullNode.instance) + ) + } +} + +data class DataClassWithAllNullableParams( + val intValue: Int?, + val stringValue: String?, + val jsonNodeValue: JsonNode?, + val jsonNodeValueProvidedNull: JsonNode?, + val jsonNodeValueWithNullAsDefault: JsonNode? = null, + val jsonNodeValueWithNullAsDefaultProvidedNull: JsonNode? = null +)