Skip to content
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

not-null requirement ignored for primitives in data classes #242

Closed
magicfoodhand opened this issue Jul 25, 2019 · 11 comments
Closed

not-null requirement ignored for primitives in data classes #242

magicfoodhand opened this issue Jul 25, 2019 · 11 comments

Comments

@magicfoodhand
Copy link

When deserializing a data class with primitive types, the default value for that java primitive type is used instead of throwing an IllegalArgumentException or MissingKotlinParameterException.

Steps to reproduce

  1. Create new project using maven archetype - org.jetbrains.kotlin:kotlin-archetype-jvm
  2. Add dependency on jackson-module-kotlin - 2.9.7
  3. Run the following test class
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.junit.Test
import java.lang.IllegalArgumentException
import kotlin.test.assertNull

data class NullIntValue(val value: Int?)
data class NullDoubleValue(val value: Double?)
data class NullLongValue(val value: Long?)
data class NullBooleanValue(val value: Boolean?)

data class IntValue(val value: Int)
data class DoubleValue(val value: Double)
data class LongValue(val value: Long)
data class BooleanValue(val value: Boolean)

class DeserializationTest {
    private val objectMapper = jacksonObjectMapper()

    @Test
    fun `test value is null - Boolean`(){
        val value : NullBooleanValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }

    @Test
    fun `test value is null - Double`(){
        val value : NullDoubleValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }

    @Test
    fun `test value is null - Int`(){
        val value : NullIntValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }

    @Test
    fun `test value is null - Long`(){
        val value : NullLongValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }

    @Test(expected = IllegalArgumentException::class)
    fun `test value throws - Boolean`(){
        val value : BooleanValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }

    @Test(expected = IllegalArgumentException::class)
    fun `test value throws - Double`(){
        val value : DoubleValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }

    @Test(expected = IllegalArgumentException::class)
    fun `test value throws - Int`(){
        val value : IntValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }

    @Test(expected = IllegalArgumentException::class)
    fun `test value throws - Long`(){
        val value : LongValue = objectMapper.readValue("{}")
        assertNull(value.value)
    }
}
@mikeholler
Copy link

I'm also hitting this.

@mikeholler
Copy link

@JsonProperty(required = true) seems to help.

@cowtowncoder
Copy link
Member

I think that it should be possible to make Kotlin-specific AnnotationIntrospector "find" required-ness for properties of data classes.

@serandel
Copy link

serandel commented Sep 6, 2020

I've hit this as well :(

Any work on this?

@Nayacco
Copy link

Nayacco commented Mar 8, 2021

Is this issue the content of this PR repair? #41

Just configure Jackson will solve the problem

jacksonObjectMapper().enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)

You can simply configure it in spring

spring:
  jackson:
    deserialization:
      FAIL_ON_NULL_FOR_PRIMITIVES: true

@k163377
Copy link
Contributor

k163377 commented Feb 22, 2023

This is the default behavior of databind and it seems inappropriate for kotlin-module to intervene in that process.
However, I agree that it is counter-intuitive behavior.

@cowtowncoder
Any ideas on this issue?
Is enabling FAIL_ON_NULL_FOR_PRIMITIVES as GoldSubmarine suggests the only effective solution?

@cowtowncoder
Copy link
Member

I think my only comment is that if Kotlin module could determine "requiredness" for properties, that could be used to force checks.
Method in AnnotationIntrospector is:

    @Override
    public Boolean hasRequiredMarker(AnnotatedMember m) {
    }

and for core Jackson checks for @JsonProperty(required = true) but for Kotlin could use other mechanisms.

@k163377
Copy link
Contributor

k163377 commented Sep 10, 2023

It was closed as a duplicate of FasterXML/jackson-databind#3392

@k163377 k163377 closed this as completed Sep 10, 2023
@k163377
Copy link
Contributor

k163377 commented Sep 10, 2023

I share a Kotlin version of the sample workaround shared below.
FasterXML/jackson-databind#3392 (comment)

    override fun refineDeserializationType(config: MapperConfig<*>, a: Annotated, baseType: JavaType): JavaType {
        return if (a is AnnotatedParameter && baseType.isPrimitive) {
            // As a workaround to the problem reported below,
            // if the parameter is a primitive type, deserialize it as a wrapper type.
            // https://github.com/FasterXML/jackson-module-kotlin/issues/242
            config.typeFactory.constructType(baseType.rawClass.kotlin.javaObjectType)
        } else {
            baseType
        }
    }

k163377 added a commit to k163377/jackson-module-kotlin that referenced this issue Sep 10, 2023
k163377 added a commit that referenced this issue Sep 10, 2023
@Serheynix
Copy link

Perhaps my solution will help someone.

What is my problem?

data class NullIntValue(val value: Int? = 123)
val value : NullIntValue = objectMapper.readValue("{\"value\": null}")
print(value.value) // 123 Correct for me

data class IntValue(val value: Int = 123)
val value : IntValue = objectMapper.readValue("{\"value\": null}")
print(value.value) // 0 PROBLEM

I don't want to get an exception, the option DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES doesn't suit me

SOLUTION (for my jackson-module-kotlin:2.18)

mapper.setDefaultSetterInfo(JsonSetter.Value.construct(Nulls.SKIP, Nulls.DEFAULT))
or
data class IntValue(@JsonSetter(nulls = Nulls.SKIP) val value: Int = 123)

PS. I don't feel comfortable writing JsonSetter annotation for all primitive types. So I prefer to use setDefaultSetterInfo. Let me know if there is a better solution to my kotlin problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants