Why @JsonIgnore annotation doesn't work during deserializing data?

I have a data https://gist.githubusercontent.com/iva-nova-e-katerina/fc1067e971c71a73a0b525a21b336694/raw/954477261bb5ac2f52cee07a8bc45a2a27de1a8c/data2.json a List with seven CheckResultItem elements. I trying to parse them this way:

import com.fasterxml.jackson.module.kotlin.readValue
...
val res = restHelper.objectMapper.readValue<List<CheckResultItem>>(text) 

which gives me the following error:

com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.fmetric.validation.api.Brick] value failed for JSON property upperLevelBricks due to missing (therefore NULL) value for creator parameter upperLevelBricks which is a non-nullable type
 at [Source: (StringReader); line: 1, column: 714] (through reference chain: java.util.ArrayList[0]->com.fmetric.validation.api.checking.CheckResultItem["brick"]->com.fmetric.validation.api.Brick["upperLevelBricks"])

    at com.fasterxml.jackson.module.kotlin.KotlinValueInstantiator.createFromObjectWith(KotlinValueInstantiator.kt:116)

There is @JsonIgnore annotation in data class :

data class Brick(
        val id: UUID?,
        val name: String,
        val type: BrickType,
        val propertyValues: List<ProjectBrickPropertyValue<*>>,

        @JsonIgnore
        val upperLevelBricks: ArrayList<Brick>,
        val downLevelBricks: ArrayList<Brick>,    
        var drawingDetails: List<BrickDrawingDetails>?

) {

But it seems it doesn't work. Could you explain me what is wrong?

UPD: Also I have tried @JsonIgnoreProperties({"upperLevelBricks"}) class annotation but it doesn't work. My solution was to set a default value

val upperLevelBricks: ArrayList<Brick> = arrayListOf(),

But I think that annotations should work!

Upvotes: 0

Views: 1822

Answers (1)

Actually, it works, but not the way you think. During deserialization @JsonIgnore ignores the respectful field in JSON, like it wasn't there (but it's doesn't make sense in this case, because it's initially absent in JSON).

In Java, Jackson would've just instantiated class with null value for the absent field (because all object types in Java are nullable, which means they allow the value to be set to null). But in Kotlin, a property should be explicitly marked as nullable (val upperLevelBricks: List<Brick>?) or have a default value (val upperLevelBricks: List<Brick> = emptyList()) so that Jackson could create a class instance in this case.

Note that approach with default value for property won't work (unless you additionally mark it with @JsonIgnore) if this field is present in JSON but explicitly set to null:

{
  ...
  "upperLevelBricks": null,
  ...
}

Anyway, if you don't want to change the API of your Brick class you may provide a default value for this field only when it's created during Jackson deserialization (and only if it's absent/null in JSON) via custom deserializer:

object EmptyListAsDefault : JsonDeserializer<List<Brick>>() {
    override fun deserialize(jsonParser: JsonParser, context: DeserializationContext): List<Brick> =
        jsonParser.codec.readValue(
            jsonParser,
            context.typeFactory.constructCollectionType(List::class.java, Brick::class.java)
        )

    override fun getNullValue(context: DeserializationContext): List<Brick> = emptyList()
}

data class Brick(
    //...
    @JsonDeserialize(using = EmptyListAsDefault::class)
    val upperLevelBricks: List<Brick>,
    //...
)

Upvotes: 1

Related Questions