Cannot serialize/deserialize bidirectional references using Jackson JSON in Java?

41 views Asked by At

I have intentionally created complex Java classes hierarchy, which also can occur on production in order to test Jackson JSON serialization and deserialization. Some annotations, like @Getter are Lombok ones.

DemoI.java

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "interface",
        defaultImpl = DemoImpl.class
)
@JsonSubTypes(
        @JsonSubTypes.Type(value = DemoImpl.class, name = "demoImpl")
)
public interface DemoI {
}

DemoImpl.java

public class DemoImpl implements DemoI {
    @Getter
    private int age;
    @Getter
    private String name;
    @Getter
    private DemoAbst demoAbst;

    @JsonCreator
    public DemoImpl(@JsonProperty("age") int age,
                    @JsonProperty("name") String name,
                    @JsonProperty("demoAbst") DemoAbst demoAbst) {
        this.age = age;
        this.name = name;
        this.demoAbst = demoAbst;
    }
}

DemoAbst.java

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "abstract",
        defaultImpl = DemoAbstImpl.class
)
@JsonSubTypes(
        @JsonSubTypes.Type(value = DemoAbstImpl.class, name = "demoAbstImpl")
)
public abstract class DemoAbst {
}

DemoAbstImpl.java

@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id"
)
public class DemoAbstImpl extends DemoAbst{
    @Getter
    private final int id = 1;
    @Getter
    private int song;
    @Getter
    private final DemoAbstImplImpl demoAbstImplImpl;

    public DemoAbstImpl(int song) {
        this.song = song;
        this.demoAbstImplImpl = new DemoAbstImplImpl("song", this);
    }

    @JsonCreator
    public DemoAbstImpl(@JsonProperty("song") int song,
                        @JsonProperty("demoAbstImplImpl") DemoAbstImplImpl d) {
        this.song = song;
        this.demoAbstImplImpl = d;
    }

}

DemoAbstImplImpl.java

public class DemoAbstImplImpl {
    @Getter
    private String koo;
    @Getter
    private final DemoAbstImpl demoAbstImpl;

    @JsonCreator
    public DemoAbstImplImpl(@JsonProperty("koo") String koo,
                            @JsonProperty("demoAbstImpl") DemoAbstImpl d) {
        this.koo = koo;
        this.demoAbstImpl = d;
    }
}

And finally, the Main.java

public class Demo {
    public static void main(String[] args) throws IOException {

        DemoAbstImpl dai = new DemoAbstImpl(67);

        DemoI di = new DemoImpl(1, "Hello", dai);


        ObjectMapper om = new ObjectMapper();
        byte[] b = om.writeValueAsBytes(di);
        Files.write(Path.of("out.json"), b);

        DemoI di2 = om.readValue(new File("out.json"), DemoI.class);

When I serialize, I got following JSON

{
  "interface": "demoImpl",
  "age": 1,
  "name": "Hello",
  "demoAbst": {
    "abstract": "demoAbstImpl",
    "id": 1,
    "song": 67,
    "demoAbstImplImpl": {
      "koo": "song",
      "demoAbstImpl": 1,
      "id": 2
    }
  }
}

For me, that demoAbstImpl: 1 doesn't make sense at all, seems that Jackson uses it as a reference, but why Jackson doesn't follow that reference till the end, i.e until it reaches the value.

And also deserialization ends up with an error Exception in thread "main" com.fasterxml.jackson.databind.deser.UnresolvedForwardReference: Could not resolve Object Id [1] (for [simple type, com.example.mytest.DemoAbstImpl]).

Any ideas, how to fix that? At least for me, the code seems very correct, and no issues should be with Jackson annotations.

0

There are 0 answers