Deserialising Abstract Classes with JacksonΒΆ
Jackson can automatically detect the relevant abstract class to initialise when
parsing some JSON if you give it a little bit of help. There are multiple
methods Jackson can use to detect and indicate the class it should use but the
method I have chosen is to tell it to look for a type
field.
Consider the following json snippets
{
"id": "123",
"type": "name",
"name": "Alex"
}
{
"id": "345",
"type": "value",
"value": 123.4
}
Which can be represented by the following classes
public class IdName extends IdField {
public static final String FIELD_TYPE = "name";
private String name;
public IdName() {
super(FIELD_TYPE);
}
...
}
public class IdValue extends IdField {
public static final String FIELD_TYPE = "value";
private float value;
public IdValue() {
super(FIELD_TYPE);
}
...
}
We can then add a few annotations to the base abstract class telling Jackson to
check the value of the type
field and have it pick the correct concrete class
automatically.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = IdName.class, name = IdName.FIELD_TYPE),
@JsonSubTypes.Type(value = IdValue.class, name = IdValue.FIELD_TYPE)
})
public abstract class IdField {
private String id;
private String type;
public IdField(String type) {
this.id = UUID.randomUUID().toString();
this.type = type;
}
...
}
Then we can just parse the json as we normally would.
String jsonStr = "{\"id\":\"123\",\"name\":\"Alex\"}";
ObjectMapper mapper = new ObjectMapper();
IdField field = mapper.readValue(jsonStr, IdField.class);
System.out.println(field.getType()); // => "name"
IdName nameField = (IdName) field;
System.out.println(nameField.getName()); // => "Alex"