How to deserialize abstract types from JSON using Jackson?
2017-01-23Overview
Sometimes you need deserialize JSON to Java abstract types. Good example is list of animals.
{
"animals": [
{
"name": "Szarik",
"ageInYears": 3,
"type": "dog"
},
{
"name": "Filemon",
"livesLeft": 6,
"type": "cat"
}
]
}
In this case you need to serialize animals to specific types using type
property.
Java Data Classes (Lombock)
import lombok.Data;
interface Animal {
}
@Data
class AnimalsContainer {
private final List<Animal> animals = new LinkedList<>();
}
@Data
class Dog implements Animal {
private final String name;
private final Integer ageInYears;
}
@Data
class Cat implements Animal {
private final String name;
private final Integer livesLeft;
}
Json Type Info Annotations
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
interface Animal {
}
Please note that we use property type
to determine specific subclass of Animal
.
Test (jUnit 5)
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class AnimalsTest {
@Test
@DisplayName("it should deserialize arrays of animal to specific types")
public void animals() throws Throwable {
// given
String json = Files.readAsText("mediabus/examples/animals.json");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// when
List<Animal> animals = objectMapper.readValue(json, AnimalsContainer.class).getAnimals();
// then
assertEquals(2, animals.size());
assertEquals(Dog.class, animals.get(0).getClass());
assertEquals(3, ((Dog) animals.get(0)).getAgeInYears().intValue());
assertEquals(Cat.class, animals.get(1).getClass());
assertEquals(6, ((Cat) animals.get(1)).getLivesLeft().intValue());
}
}
System.out.println(animals);
prints [Dog(name=Szarik, ageInYears=3), Cat(name=Filemon, livesLeft=6)]
.
Links
- http://programmerbruce.blogspot.ca/2011/05/deserialize-json-with-jackson-into.html
- http://stackoverflow.com/questions/30362446/deserialize-json-with-jackson-into-polymorphic-types-a-complete-example-is-giv
- http://stackoverflow.com/questions/1325074/using-jackson-objectmapper-to-serialize-the-subclass-name-into-json-not-the-sup
- http://stackoverflow.com/questions/31665620/is-jacksons-jsonsubtypes-still-necessary-for-polymorphic-deserialization