Skip to content

Correctly deserialize forward @JsonIdentityInfo references when using @JsonCreator #3030

Description

@benediktsatalia

Is your feature request related to a problem? Please describe.
Currently we cannot use forward references together with a @JsonCreator. See the following example:

class A1 {
  public List<B> bs;
  public List<C1> cs;
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class B {
  public String id;
}

class C1 {
  private B b;

  @JsonCreator
  public C1(@JsonProperty("b") B b) {
    this.b = b;
  }

  @JsonGetter("b")
  public B getB() {
    return b;
  }
}

class C2 {
  public B b;
}

class A2 {
  public List<B> bs;
  public List<C2> cs;
}

public class Main {

  public static void main(final String[] args) throws IOException {
    ObjectMapper mapper = new ObjectMapper();

    A1 a2 = mapper.readValue("{\"bs\":[{\"id\":\"b1\"},{\"id\":\"b2\"}],\"cs\":[{\"b\":\"b1\"},{\"b\":\"b2\"}]}", A1.class);
    System.out.println("Using correct ordered data (no forward references) with JsonCreator works");
    A2 a3 = mapper.readValue("{\"cs\":[{\"b\":\"b1\"},{\"b\":\"b2\"}],\"bs\":[{\"id\":\"b1\"},{\"id\":\"b2\"}]}", A2.class);
    System.out.println("Using incorrect ordered data (with forward references) with no JsonCreator works");
    A1 a4 = mapper.readValue("{\"cs\":[{\"b\":\"b1\"},{\"b\":\"b2\"}],\"bs\":[{\"id\":\"b1\"},{\"id\":\"b2\"}]}", A1.class);
  }
}

The first two deserializations work as expected but the third one, where we use forward references and the @JsonCreator throws an exception.

This is the output when running the above code snippet:

Using correct ordered data (no forward references) with JsonCreator works
Using incorrect ordered data (with forward references) with no JsonCreator works
Exception in thread "main" com.fasterxml.jackson.databind.deser.UnresolvedForwardReference: Could not resolve Object Id [b1] (for [simple type, class B]).
 at [Source: (String)"{"cs":[{"b":"b1"},{"b":"b2"}],"bs":[{"id":"b1"},{"id":"b2"}]}"; line: 1, column: 17] (through reference chain: A1["cs"]->java.util.ArrayList[0]->C1["b"])
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectId(BeanDeserializerBase.java:1292)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1381)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:176)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:166)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:535)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:419)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:285)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:293)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3434)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3402)
	at Main.main(Main.java:52)

Describe the solution you'd like
Lazily call @JsonCreator for such objects only after the whole json (and all objects with ids) got read.

Usage example
See my example above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

3.2has-failing-testIndicates that there exists a test case (under `failing/`) to reproduce the issueobjectid

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions