I have researched for the best way to implement Objects classes with inheritance and contained within an object as a List.
- Link1: How to configure Jackson to deserialize named types with default typing?
- Link2: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization
- Link3: http://www.davismol.net/2015/03/05/jackson-json-deserialize-a-list-of-objects-of-subclasses-of-an-abstract-class/
If you know any link which handles the scenario and provides the correct answer I will be happy to accept this as duplicate and close this item. I have following classes:
Zoo
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import lombok.*;
@Getter
@Setter
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Zoo {
private String zooName;
private String zooLocation;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private @Singular List<Animal> animals;
}
Animal
@Getter
@Setter
@Builder(builderMethodName = "parentBuilder")
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NONE,
include = JsonTypeInfo.As.PROPERTY,
property = "type",
defaultImpl = Animal.class
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Elephant.class, name = "elephant")
})
public class Animal{
private String type;
private String nameTag;
}
Dog
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonPropertyOrder({ "type", "nameTag"})
@JsonTypeName("dog")
public class Dog extends Animal{
private String barkingPower;
}
Elephant
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonPropertyOrder({ "type", "nameTag"})
public class Elephant extends Animal{
private String weight;
}
ModelHelper
public final class ModelHelper {
private static final ObjectMapper MAPPER = new ObjectMapper();
.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.enableDefaultTypingAsProperty(
ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, "type");
public static <T> T fromJson(@NotNull final String json, final Class<T> objectClass) {
try {
return MAPPER.readValue(json, objectClass);
} catch (Exception e) {
final String message = String.format("Error de-serialising JSON '%s' to object of %s",
json, objectClass.getName());
log.warn(message, e);
return null;
}
}
}
And I am using the ModelHelper to deserialize the JSON to an Object:
String json = "{\"zooName\":\"Sydney Zoo\",\"zooLocation\":\"Sydney\",\"animals\":[{\"type\":\"dog\",\"nameTag\":\"Dog1\",\"barkingPower\":\"loud\"},{\"type\":\"elephant\",\"nameTag\":\"Elephant1\",\"weight\":\"heavy\"}]}";
mapper.readValue(json, Zoo.class);
Currently the deserialization of Zoo only comes back with Animal attributes and not with Dog or Elephant attributes.
My Questions are:
- What I can gather is deserialization is not working because the getter in
ZooforList<Animal>is of base class type and deserialization is not able to work out how to create aDogorElephant, and based on signature it generatesAnimal. But I would have thought that by puttingJsonTypeNameandJsonSubTypesannotation I have marked the relevant subclasses. is this the case? - Does
Animalclass has to be defined asabstractfor this to work? - Is the only way to get this to work is to implement custom deserialization for class Zoo? Is this a good example to follow: https://www.sghill.net/how-do-i-write-a-jackson-json-serializer-deserializer.html?
Let me know if the code is not clear and I will fix it.