Skip to content

Commit

Permalink
Add module configuration flag to treat null values as defaults instea…
Browse files Browse the repository at this point in the history
…d of doing this without warning.
  • Loading branch information
apatrida committed Oct 26, 2019
1 parent 2552fd7 commit 45676ce
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.*


internal class KotlinAnnotationIntrospector(private val context: Module.SetupContext, private val cache: ReflectionCache, private val nullToEmptyCollection: Boolean, private val nullToEmptyMap: Boolean) : NopAnnotationIntrospector() {
internal class KotlinAnnotationIntrospector(private val context: Module.SetupContext,
private val cache: ReflectionCache,
private val nullToEmptyCollection: Boolean,
private val nullToEmptyMap: Boolean,
private val nullIsSameAsDefault: Boolean) : NopAnnotationIntrospector() {

// TODO: implement nullIsSameAsDefault flag, which represents when TRUE that if something has a default value, it can be passed a null to default it
// this likely impacts this class to be accurate about what COULD be considered required

override fun hasRequiredMarker(m: AnnotatedMember): Boolean? =
cache.javaMemberIsRequired(m) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fun Class<*>.isKotlinClass(): Boolean {
return declaredAnnotations.any { it.annotationClass.java.name == metadataFqName }
}

class KotlinModule(val reflectionCacheSize: Int = 512, val nullToEmptyCollection: Boolean = false, val nullToEmptyMap: Boolean = false) : SimpleModule(PackageVersion.VERSION) {
class KotlinModule(val reflectionCacheSize: Int = 512, val nullToEmptyCollection: Boolean = false, val nullToEmptyMap: Boolean = false, val nullisSameAsDefault: Boolean = false) : SimpleModule(PackageVersion.VERSION) {
companion object {
const val serialVersionUID = 1L
}
Expand All @@ -41,12 +41,12 @@ class KotlinModule(val reflectionCacheSize: Int = 512, val nullToEmptyCollection

val cache = ReflectionCache(reflectionCacheSize)

context.addValueInstantiators(KotlinInstantiators(cache, nullToEmptyCollection, nullToEmptyMap))
context.addValueInstantiators(KotlinInstantiators(cache, nullToEmptyCollection, nullToEmptyMap, nullisSameAsDefault))

// [module-kotlin#225]: keep Kotlin singletons as singletons
context.addBeanDeserializerModifier(KotlinBeanDeserializerModifier)

context.insertAnnotationIntrospector(KotlinAnnotationIntrospector(context, cache, nullToEmptyCollection, nullToEmptyMap))
context.insertAnnotationIntrospector(KotlinAnnotationIntrospector(context, cache, nullToEmptyCollection, nullToEmptyMap, nullisSameAsDefault))
context.appendAnnotationIntrospector(KotlinNamesAnnotationIntrospector(this, cache))

context.addDeserializers(KotlinDeserializers())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ internal class KotlinValueInstantiator(
src: StdValueInstantiator,
private val cache: ReflectionCache,
private val nullToEmptyCollection: Boolean,
private val nullToEmptyMap: Boolean
private val nullToEmptyMap: Boolean,
private val nullIsSameAsDefault: Boolean
) : StdValueInstantiator(src) {
@Suppress("UNCHECKED_CAST")
override fun createFromObjectWith(
Expand Down Expand Up @@ -91,7 +92,7 @@ internal class KotlinValueInstantiator(

var paramVal = if (!isMissing || paramDef.isPrimitive() || jsonProp.hasInjectableValueId()) {
val tempParamVal = buffer.getParameter(jsonProp)
if (tempParamVal == null && paramDef.isOptional) {
if (nullIsSameAsDefault && tempParamVal == null && paramDef.isOptional) {
return@forEachIndexed
}
tempParamVal
Expand Down Expand Up @@ -159,7 +160,8 @@ internal class KotlinValueInstantiator(
internal class KotlinInstantiators(
private val cache: ReflectionCache,
private val nullToEmptyCollection: Boolean,
private val nullToEmptyMap: Boolean
private val nullToEmptyMap: Boolean,
private val nullIsSameAsDefault: Boolean
) : ValueInstantiators {
override fun findValueInstantiator(
deserConfig: DeserializationConfig,
Expand All @@ -168,7 +170,7 @@ internal class KotlinInstantiators(
): ValueInstantiator {
return if (beanDescriptor.beanClass.isKotlinClass()) {
if (defaultInstantiator is StdValueInstantiator) {
KotlinValueInstantiator(defaultInstantiator, cache, nullToEmptyCollection, nullToEmptyMap)
KotlinValueInstantiator(defaultInstantiator, cache, nullToEmptyCollection, nullToEmptyMap, nullIsSameAsDefault)
} else {
// TODO: return defaultInstantiator and let default method parameters and nullability go unused? or die with exception:
throw IllegalStateException("KotlinValueInstantiator requires that the default ValueInstantiator is StdValueInstantiator")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.fasterxml.jackson.module.kotlin.test

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
Expand All @@ -11,7 +13,7 @@ import org.junit.internal.runners.statements.ExpectException
// @Ignore("not yet implemented, under discussion")
class NullToDefault {

private fun createMapper() = jacksonObjectMapper()
private fun createMapper(allowDefaultingByNull: Boolean) = ObjectMapper().registerModule(KotlinModule(nullisSameAsDefault = allowDefaultingByNull))

private data class TestClass(val sku: Int = -1,
val text: String,
Expand All @@ -22,8 +24,29 @@ class NullToDefault {
val order: Int = -1)

@Test
fun shouldUseDefault() {
val item = createMapper().readValue<TestClass>(
fun shouldUseNullAsDefault() {
val item = createMapper(true).readValue<TestClass>(
"""{
"sku": "974",
"text": "plain",
"name": null,
"images": null,
"attribute": "19"
}""")

Assert.assertTrue(item.sku == 974)
Assert.assertTrue(item.text == "plain")
@Suppress("SENSELESS_COMPARISON")
Assert.assertTrue(item.name != null)
Assert.assertTrue(item.images == null)
Assert.assertTrue(item.language == "uk")
Assert.assertTrue(item.attribute == 19)
Assert.assertTrue(item.order == -1)
}

@Test(expected = MissingKotlinParameterException::class)
fun shouldNotUseNullAsDefault() {
val item = createMapper(false).readValue<TestClass>(
"""{
"sku": "974",
"text": "plain",
Expand All @@ -44,7 +67,7 @@ class NullToDefault {

@Test(expected = MissingKotlinParameterException::class)
fun errorIfNotDefault() {
val item = createMapper().readValue<TestClass>(
val item = createMapper(true).readValue<TestClass>(
"""{
"sku": "974",
"text": null,
Expand Down

0 comments on commit 45676ce

Please sign in to comment.