jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

Deserialization issue: MismatchedInputException, Bean not yet resolved

Open detomarco opened this issue 2 years ago β€’ 29 comments
trafficstars

Description

When I try to deserialize this object

{
	"id": 1,
	"fruits": [{
		"id": 2,
		"tree": 1,
		"calories": [{
			"id": 3,
			"fruit": 2
		}]
	}]
}

I get this error

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot resolve ObjectId forward reference using property 'tree' (of type demo.jackson.Fruit): Bean not yet resolved
 at [Source: (String)"{
              "id": 1,
              "fruits": [
                {
                  "id": 2,
                  "tree": 1,
                  "calories": [
                    {
                      "id": 3,
                      "fruit": 2
                    }
                  ]
                }
              ]
            }"; line: 15, column: 13] (through reference chain: demo.jackson.Fruit["tree"])
	at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
	at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1482)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer$BeanReferring.handleResolvedForwardReference(BeanDeserializer.java:1090)
	at com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.bindItem(ReadableObjectId.java:64)
	at com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer.handleIdValue(PropertyValueBuffer.java:251)
	at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:207)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:424)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1322)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1288)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:162)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4526)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3468)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3436)
	at demo.jackson.LocationJavaTest.lambda$jsonDeserializeTree$0(LocationJavaTest.java:51)
	at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:55)
	at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:37)
	at org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3082)
	at demo.jackson.LocationJavaTest.jsonDeserializeTree(LocationJavaTest.java:50)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)

Version information

Current version: v2.11.4 It works with v2.11.3

Release note v2.11.4: https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.11.4 Issue probably created by #2944

To Reproduce

Demo jackson_parsing.zip

the project contains a unit test in order to reproduce the bug

Other information

It might be related to https://github.com/FasterXML/jackson-databind/issues/2152 but it has been created 2 years before the version 2.11.4 has been released

detomarco avatar Jun 04 '23 16:06 detomarco

I know there's link to full repro, but would it be possible to inline POJO class definition for target class? It'd be bit more convenient to see if this can be reproduced.

Especially as it was mentioned that @JsonCreator is being used -- which may be why this is not (and perhaps, cannot be) supported.

cowtowncoder avatar Jun 05 '23 01:06 cowtowncoder

@cowtowncoder sure, these are the pojo classes (kotlin)

@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator::class,
    property = "id",
    scope = Tree::class,
)
data class Tree(
    val id: Int,
    var fruits: MutableList<Fruit> = mutableListOf(),
)

@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator::class,
    property = "id",
    scope = Fruit::class,
)
data class Fruit(
    val id: Long,
    var calories: List<Calories> = emptyList(),
) {
    var tree: Tree? = null
}

@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator::class,
    property = "id",
    scope = Calories::class,
)
data class Calories(
    val id: Int,
) {
    var fruit: Fruit? = null
}

and this is the json I'm trying to deserialize

{
	"id": 1,
	"fruits": [{
		"id": 2,
		"tree": 1,
		"calories": [{
			"id": 3,
			"fruit": 2
		}]
	}]
}

detomarco avatar Jun 05 '23 07:06 detomarco

@detomarco does this still happen with 2.15? 2.11 is long out of support. Also, please try making a reproducer with java only instead of kotlin, to confirm whether this is an issue specific to the kotlin module

yawkat avatar Jun 05 '23 07:06 yawkat

yes, still happening with the latest version.

I assumed that it depends on this module because I found the specific PR (https://github.com/FasterXML/jackson-databind/pull/2958) that breaks the code

detomarco avatar Jun 05 '23 08:06 detomarco

i just converted your test case to java and it works fine:

    public static void main(String[] args) throws JsonProcessingException {
        new ObjectMapper().readValue("{\n" +
                "\t\"id\": 1,\n" +
                "\t\"fruits\": [{\n" +
                "\t\t\"id\": 2,\n" +
                "\t\t\"tree\": 1,\n" +
                "\t\t\"calories\": [{\n" +
                "\t\t\t\"id\": 3,\n" +
                "\t\t\t\"fruit\": 2\n" +
                "\t\t}]\n" +
                "\t}]\n" +
                "}", Tree.class);
    }

    @JsonIdentityInfo(
            generator = ObjectIdGenerators.PropertyGenerator.class,
            property = "id",
            scope = Tree.class
    )
    public static class Tree {
        private final int id;
        private List<Fruit> fruits = Collections.emptyList();

        @JsonCreator
        public Tree(@JsonProperty("id") int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }

        public List<Fruit> getFruits() {
            return fruits;
        }

        public void setFruits(List<Fruit> fruits) {
            this.fruits = fruits;
        }
    }

    @JsonIdentityInfo(
            generator = ObjectIdGenerators.PropertyGenerator.class,
            property = "id",
            scope = Fruit.class
    )
    public static class Fruit {
        private final long id;
        private List<Calories> calories = Collections.emptyList();
        private Tree tree;

        @JsonCreator
        public Fruit(@JsonProperty("id") long id) {
            this.id = id;
        }

        public long getId() {
            return id;
        }

        public List<Calories> getCalories() {
            return calories;
        }

        public void setCalories(List<Calories> calories) {
            this.calories = calories;
        }

        public Tree getTree() {
            return tree;
        }

        public void setTree(Tree tree) {
            this.tree = tree;
        }
    }

    @JsonIdentityInfo(
            generator = ObjectIdGenerators.PropertyGenerator.class,
            property = "id",
            scope = Calories.class
    )
    public static class Calories {
        private final int id;
        private Fruit fruit;

        @JsonCreator
        public Calories(@JsonProperty("id") int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }

        public Fruit getFruit() {
            return fruit;
        }

        public void setFruit(Fruit fruit) {
            this.fruit = fruit;
        }
    }

yawkat avatar Jun 05 '23 09:06 yawkat

I will open the same issue in the kotlin module then. Thank you

detomarco avatar Jun 05 '23 13:06 detomarco

I will open the same issue in the kotlin module then. Thank you

Sounds like a planπŸ‘πŸΌ Maybe we can reopen this issue if needed, @detomarco ? 😊

JooHyukKim avatar Jun 05 '23 13:06 JooHyukKim

sure, I close this issue for now. Thank you for your support!!

detomarco avatar Jun 05 '23 14:06 detomarco

This is the issue I've open in the kotlin module https://github.com/FasterXML/jackson-module-kotlin/issues/681

detomarco avatar Jun 05 '23 14:06 detomarco

I reopen since the errors occurs also in plain Java as described here https://github.com/FasterXML/jackson-module-kotlin/issues/681#issuecomment-1583006169

public class Main {
    @JsonIdentityInfo(
            generator = ObjectIdGenerators.PropertyGenerator.class,
            property = "id",
            scope = Tree.class
    )
    public static class Tree {
        private final int id;
        private List<Fruit> fruits;

        @JsonCreator
        public Tree(@JsonProperty("id") int id, @JsonProperty("fruits") List<Fruit> fruits) {
            this.id = id;
            this.fruits = fruits;
        }

        public int getId() {
            return id;
        }

        public List<Fruit> getFruits() {
            return fruits;
        }

        public void setFruits(List<Fruit> fruits) {
            this.fruits = fruits;
        }
    }

    @JsonIdentityInfo(
            generator = ObjectIdGenerators.PropertyGenerator.class,
            property = "id",
            scope = Fruit.class
    )
    public static class Fruit {
        private final int id;
        private List<Calories> calories;
        @JsonBackReference("id")
        private Tree tree;

        @JsonCreator
        public Fruit(@JsonProperty("id") int id, @JsonProperty("calories") List<Calories> calories) {
            this.id = id;
            this.calories = calories;
        }

        public int getId() {
            return id;
        }

        public Tree getTree() {
            return tree;
        }

        public void setTree(Tree tree) {
            this.tree = tree;
        }

        public List<Calories> getCalories() {
            return calories;
        }

        public void setCalories(List<Calories> calories) {
            this.calories = calories;
        }
    }

    @JsonIdentityInfo(
            generator = ObjectIdGenerators.PropertyGenerator.class,
            property = "id",
            scope = Calories.class
    )
    public static class Calories {
        private final int id;
        private Fruit fruit;

        @JsonCreator
        public Calories(@JsonProperty("id") int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }

        public Fruit getFruit() {
            return fruit;
        }

        public void setFruit(Fruit fruit) {
            this.fruit = fruit;
        }
    }

    private static final ObjectMapper jackson = new ObjectMapper().findAndRegisterModules()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    public static void main(String[] args) throws JsonProcessingException {
        String json = "{" +
                "              \"id\": 1,\n" +
                "              \"fruits\": [\n" +
                "                {\n" +
                "                  \"id\": 2,\n" +
                "                  \"tree\": 1,\n" +
                "                  \"calories\": [\n" +
                "                    {\n" +
                "                      \"id\": 3,\n" +
                "                      \"fruit\": 2\n" +
                "                    }\n" +
                "                  ]\n" +
                "                }\n" +
                "              ]\n" +
                "            }";

        Tree result = jackson.readValue(json, Tree.class);
        System.out.println();
    }
}

I've tried personally and the deserialisation works with version v2.11.3 and breaks since v2.11.4 till the latest one

detomarco avatar Jun 09 '23 08:06 detomarco

Ok. One of first things to change in test would be:

.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

since this setting invariably hides other issues. I also notice use of @JsonBackReference with @JsonIdentityInfo and am suspicious of these two working together (they are overlapping features).

From ref to 2.11.3/4, looking at release notes I agree that #2944 looks suspicious

Aside from that, WDYT @JooHyukKim ?

cowtowncoder avatar Jun 13 '23 01:06 cowtowncoder

@detomarco Thanks for your time and efforts in discussing the kotlin-module issue. πŸ™πŸΌ

JooHyukKim avatar Jun 13 '23 16:06 JooHyukKim

@JooHyukKim, what do you think?

Hmmm... The provided JSON object itself or the combination of @JsonBackReference and @JsonIdentityInfo is not something very unusual.

Then I made a PR in #3983 to provide a simplified test version that anyone can jump in and work on. While working on it, I noticed an excessive use of the value "id" and interestingly, the test without @JsonCreator passes in #3983.

So thinking futher on this, suspicions are..

  1. it possible that the @JsonCreator mechanism consumes the JSON value of "id" before it reaches the @JsonBackReference + @JsonIdentityInfo mechanisms, @cowtowncoder?
  2. Or maybe just too many "id" defined?

JooHyukKim avatar Jun 13 '23 16:06 JooHyukKim

@JooHyukKim Combination of @JsonCreator (or rather use of constructor-passing) and @JsonIdentityInfo or @JsonBackReference is fundamentally challenging, because Object can only have identity after being constructed. So there's a chance this could be potentially case that is unsupportable without major rewriting of Object Id handling -- or in some cyclic cases, if we had dependency between 2 Objects that require use of constructors, impossible to support.

But first things first; thank you for the test case, I merged it in.

cowtowncoder avatar Jun 13 '23 18:06 cowtowncoder

@detomarco Please refer above for the analysis of current situation, esp. one about

 @JooHyukKim Combination of @JsonCreator (or rather use of constructor-passing) and @JsonIdentityInfo or @JsonBackReference is fundamentally challenging,

....... part. @detomarco your use case might not be possible to support πŸ€”. If you would like to tackle the problem, here is test case #3983

Or my other (careful) suggestion is removing @JsonCreator part like here

JooHyukKim avatar Jun 14 '23 11:06 JooHyukKim

May I suggest changing the issue title as...

 `@JsonCreator` combined with `@JsonIdentityInfo`(or `@JsonBackReference`) fails deserialization with `MismatchedInputException :  Bean not yet resolved`

.... this, @cowtowncoder ? For issue tracking, etc...

JooHyukKim avatar Jun 14 '23 11:06 JooHyukKim

Thank you so much for your investigations guys!!

Sorry but I'm a bit lost :D

In my understanding the problem is the combination of several annotations. But in my original Kotlin code I did not add any of them, so I assume the problem is again in the jackson-kotlin module which adds those annotations. Did I get it right? In that case I will reopen the issue there again

@JooHyukKim Combination of @JsonCreator (or rather use of constructor-passing) and @JsonIdentityInfo or @JsonBackReference is fundamentally challenging, because Object can only have identity after being constructed. So there's a chance this could be potentially case that is unsupportable without major rewriting of Object Id handling -- or in some cyclic cases, if we had dependency between 2 Objects that require use of constructors, impossible to support.

But first things first; thank you for the test case, I merged it in.

it's not really clear to me why could be impossible to support since it was working well till v2.11.3

detomarco avatar Jun 14 '23 13:06 detomarco

But in my original Kotlin code I did not add any of them, so I assume the problem is again in the jackson-kotlin module which adds those annotations. Did I get it right?

Yes, it "seems" like kotlin-module is where the annotation is added --sadly I have no knowedge how Kotlin at the moment.

In that case I will reopen the issue there again

That will be awesome, thank you again @detomarco !

it's not really clear to me why could be impossible to support since it was working well till v2.11.3

Sadly, though the community tries the best to not introduce regression (if this is one), it can happen.

JooHyukKim avatar Jun 14 '23 13:06 JooHyukKim

There is nothing that kotlin-module can do to fix this problem.

The kotlin-module basically marks the constructor as JsonCreator. This is because if you simply use the data class, there are no argumentless constructors or setters there (I recommend checking the decompiled results). And kotlin-module does not do any processing related to JsonIdentityInfo.

k163377 avatar Jun 14 '23 13:06 k163377

Ok so this may be a limitation of Kotlin Data classes (and similar immutable value classes like Java Records) wrt @JsonIdentityInfo -- not necessarily in all cases, but for some, in particular references between 2 immutable value classes.

I am not saying this is definitely the case here but as a sort of concern that it might be.

In plain Java one way to work around issues like this are to make Object Id properties mutable, not passed via constructor (so they can be "late-bound" in a way, to remove cycle) but this is not available for immutable value classes.

cowtowncoder avatar Jun 15 '23 15:06 cowtowncoder

Hi guys, so what is the outcome so far? Sorry I did not understand

detomarco avatar Jun 19 '23 19:06 detomarco

@detomarco It is not clear if this can be fixed or not; is it a fundamental limitation, or something that could be fixed.

cowtowncoder avatar Jun 19 '23 20:06 cowtowncoder

@detomarco So it seems like kotlin-module may not have solution for this. And there is a workaround in plain Java. But no further suggestion for resolution seems to have been made atm.

JooHyukKim avatar Jun 19 '23 21:06 JooHyukKim

Observation

Reverting #2958 will indeed make the error go away, so this is confirmed to be the cause (EDIT: not suggesting to revert that to fix this)

Workaround

Let Tree be created first before constructing Tree.fruits, by removing the latter from Tree's main constructor:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator::class, property = "id", scope = Tree::class)
data class Tree(val id: Int) {
    // Move here, just like Fruit.tree & Calories.fruit
    var fruits: MutableList<Fruit> = mutableListOf()
}

yihtserns avatar Jun 20 '23 18:06 yihtserns

@yihtserns Presumable breaking #2944 again tho? But perhaps this would allow finding a better fix that'd work for both cases?

cowtowncoder avatar Jun 20 '23 19:06 cowtowncoder

@cowtowncoder I'm not proposing to revert that, am just double-confirming @detomarco's suspicion. πŸ˜…

Sorry for the misleading statement (edited).

yihtserns avatar Jun 20 '23 19:06 yihtserns

@yihtserns yeah did not mean to imply that, just sort of spelled out what I think is the case. But I think it's a valid question as to whether original change could be modified.

Overall this once again leads back to my suspicion that many problems are due to incorrect linkage between CreatorProperty and other (Setter/getter/field) SettableBeanPropertyies.

cowtowncoder avatar Jun 20 '23 19:06 cowtowncoder

@yihtserns thanks for your advice, the workaround worked.

However I would suggest to find a proper solution to this problem because it prevents proper use of data classes in Kotlin or records in java 14+, and can become a big problem once they are adopted more and more

detomarco avatar Jun 28 '23 07:06 detomarco

@detomarco Thanks for the concern πŸ™πŸΌ

JooHyukKim avatar Jun 28 '23 08:06 JooHyukKim