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.