Sorry, was on a vacation. Here's the test
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonUnwrapped
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.junit.Before
import org.junit.Test
data class TestGalleryWidget(
val widgetReferenceId: String,
@JsonUnwrapped var gallery: TestGallery
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class TestGallery(
val id: String? = null,
val headline: String? = null,
val intro: String? = null,
val role: String? = null,
val images: List<TestImage>? = null
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class TestImage(
val id: String? = null,
val escenicId: String? = null,
val caption: String? = null,
val copyright: String? = null,
val crops: Map<String, String>? = null
class JacksonModuleKotlinTest {
lateinit var mapper: ObjectMapper
val gallery = TestGallery(
id = "id",
headline = "headline",
intro = "intro",
role = "role",
images = listOf(
TestImage(id = "testImage1"),
TestImage(id = "testImage2")
val validJson = """
{"widgetReferenceId":"widgetReferenceId","id":"id","headline":"headline","intro":"intro","role":"role","images":[{"id":"testImage1"},{"id":"testImage2"}]}
@Before
fun setUp() {
mapper = jacksonObjectMapper()
@Test
fun serializes() {
val result = mapper.writeValueAsString(TestGalleryWidget("widgetReferenceId", gallery))
println(result)
@Test
fun deserializes() {
val result = mapper.readValue(validJson, TestGalleryWidget::class.java)
println(result)
deserializes
test fails with:
com.fasterxml.jackson.databind.JsonMappingException: Could not find creator property with name 'gallery' (in class de.weltn24.natives.arnold.TestGalleryWidget)
at [Source:
{"widgetReferenceId":"widgetReferenceId","id":"id","headline":"headline","intro":"intro","role":"role","images":[{"id":"testImage1"},{"id":"testImage2"}]}
; line: 2, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
at com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.java:1234)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.addBeanProps(BeanDeserializerFactory.java:551)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:226)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:141)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:403)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:476)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3899)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3794)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
at de.weltn24.natives.arnold.JacksonModuleKotlinTest.deserializes(JacksonModuleKotlinTest.kt:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:253)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
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
jvassbo, nanodeath, hisui, liorhar, dvalvezon, raxityo, saifulss, DenisPavlov, Segaltair, MappyLiam, and 2 more reacted with thumbs up emoji
tomekstankowski, liorhar, chris-afterpay, brunodrugowick, thomasBousselin, and akifb reacted with thumbs down emoji
dem1tris and acsbendi reacted with confused emoji
dvalvezon, borowis, Segaltair, and MappyLiam reacted with heart emoji
All reactions
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
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
it does not work for deserializing neither. here is my code:
data class Child(val name: String)
data class Parent(@field:JsonIgnore val child: Child) {
@get:JsonUnwrapped
private val _child: Child
get() = child
error message:
java.lang.IllegalArgumentException: Instantiation of [simple type, class com.example.Parent] value failed for JSON property key due to missing (therefore NULL) value for creator parameter key which is a non-nullable type
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.example.Parent["child"])
at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3750)
at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3668)
at io.vertx.core.json.JsonObject.mapTo(JsonObject.java:106)
at com.example.AppTest.json unwrap deserialize(AppTest.kt:106)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.example.Parent] value failed for JSON property key due to missing (therefore NULL) value for creator parameter key which is a non-nullable type
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.example.Parent["child"])
at com.fasterxml.jackson.module.kotlin.KotlinValueInstantiator.createFromObjectWith(KotlinValueInstantiator.kt:107)
at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:195)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:488)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1287)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3745)
... 25 more
it seems that we have to use normal class for Parent
rather than data class
class Parent {
@JsonUnwrapped
lateinit var child: Child
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 #91 (comment)
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))
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: FasterXML/jackson-databind#1467 (PR: FasterXML/jackson-databind#4271).
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
@apatrida Not sure if I missed something in your suggestion, but I tried an attribute inside the body of the data class, but didn't work. Tried in both data classes and "regular classes". When I add an @JsonUnwrapped
attribute, the other fields from the body stop being deserialized:
@JsonIgnoreProperties(ignoreUnknown = true)
data class Foobar(
val foo: String,
var bar: String? = null
@JsonUnwrapped
var unwrappedFoobar: UnwrappedFoobar? = null
data class UnwrappedFoobar(
var unwrappedFoobar: String
If I try to deserialize this Json:
"foo": "this is foo",
"bar": "this is bar",
"unwrappedFoobar": "this is unwrappedFoobar"
Then the bar
attribute will be null :(
As per my tests, everything in the body must be either wrapped or unwrapped. If you mix both, it doesn't deserialize the unwrapped ones. Serialization is fine. Is this a different issue? If yes I can open a separate one.