jackson-module-kotlin: Jackson property annotations are not working with data classes

@JsonUnwrapped annotations are not working for data classes failing with not find creator property with name 'propertyname'. Same structure with a regular class seems to work fine.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 4
  • Comments: 15 (4 by maintainers)

Commits related to this issue

Most upvoted comments

A workaround for this, in case anyone arrives at this issue while searching for a solution to the problem: Change @JsonUnwrapped to @field:JsonUnwrapped, which will move the annotation to the field (instead of the constructor parameter) where Jackson will see it and correctly interpret it.

I couldn’t get @field:JsonUnwrapped to work. It works fine for serializing but not for deserializing.

To summarize what works and what doesn’t.

This wont work Naturally this is what you would think would work

@Embeddable
data class Sku(val sku: String)

@Entity
data class Product(@field:JsonUnwrapped val sku: Sku) {
    @field:[Id GeneratedValue(strategy = GenerationType.AUTO)]
    val id: Long? = null
}

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot define Creator property “sku” as @JsonUnwrapped: combination not yet supported

1. hack to make it work This is the next best choice for it to work but is messy.

@Embeddable
data class Sku(val sku: String)

@Entity
data class Product(@field:JsonIgnore val sku: Sku) {
    @field:[Id GeneratedValue(strategy = GenerationType.AUTO)]
    val id: Long? = null

    @get:JsonUnwrapped
    private val _sku: Sku
        get() = sku
}

2. use lateinit

I don’t consider this a solution as it violates the proper way to instantiate an object. For example, if the constructor has all value objects (non primitives) then you need to put in a dummy property placeholder for it to work with a data class.

@Embeddable
data class Sku(val sku: String)

@Entity
data class Product(val dummyPropertyToMakeThisWork: String) {
    @field:[Id GeneratedValue(strategy = GenerationType.AUTO)]
    val id: Long? = null

    @JsonUnwrapped lateinit var sku: Sku
}

The issue is obscured in older versions of Jackson, with 2.9 you now get the correct error:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Can not define Creator parameter 1 as `@JsonUnwrapped`: combination not yet supported
 at [Source: 
{"widgetReferenceId":"widgetReferenceId","id":"id","headline":"headline","intro":"intro","role":"role","images":[{"id":"testImage1"},{"id":"testImage2"}]}
    ; line: 2, column: 1]

Databind does not support JsonUnwrapped being used in a constructor or static creator method, it expects a 1-to-1 relationship between incoming parameters and available creator parameters. A solution would be to move that one property into the body of the class as a lateinit property. I show the bad, and good versions here in a test case added for this issue:

https://github.com/FasterXML/jackson-module-kotlin/blob/master/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/Github56.kt

i have only found the following “hack” to work properly and without too much dummy-fields-foobar:

data class Foobar @JsonCreator(mode = JsonCreator.Mode.DELEGATING) constructor(@JsonValue val value: String) {
...
}

coming from https://github.com/FasterXML/jackson-module-kotlin/issues/91#issuecomment-546741378

While looking for a stop-gap solution for this in GitHub, I found this (usage) rather straightforward solution for @JsonUnwrapped support in Kotlin data classes without any need in adjusting fields/annotations. Support in creators is being worked on here: https://github.com/FasterXML/jackson-databind/issues/1467 (PR: https://github.com/FasterXML/jackson-databind/pull/4271).

I am using second constructor with @JsonCreator as workaround:

data class Child(val name: String)

data class Parent(@field:JsonUnwrapped val child: Child) {
   @JsonCreator
   constructor(name: String): this(Child(name))
}

You saved my day @hotzen ! Thanks