jackson-databind: Deserialization issue: MismatchedInputException, Bean not yet resolved

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

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 29 (20 by maintainers)

Commits related to this issue

Most upvoted comments

@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

@cowtowncoder I’m not proposing to revert that, am just double-confirming @detomarco’s suspicion. 😅

Sorry for the misleading statement (edited).

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.

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

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

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

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;
        }
    }