添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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

@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.

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.