Deserializing polymorphic types with Jackson based on the presence of a unique property
This feels like something @JsonTypeInfo
and @JsonSubTypes
should be used for but I've picked through the docs and none of the properties that can be supplied quite seem to match what you're describing.
You could write a custom deserializer that uses @JsonSubTypes
' "name" and "value" properties in a non-standard way to accomplish what you want. The deserializer and @JsonSubTypes
would be supplied on your base class and the deserializer would use the "name" values to check for the presence of a property and if it exists, then deserialize the JSON into the class supplied in the "value" property. Your classes would then look something like this:
@JsonDeserialize(using = PropertyPresentDeserializer.class)
@JsonSubTypes({
@Type(name = "stringA", value = SubClassA.class),
@Type(name = "stringB", value = SubClassB.class)
})
public abstract class Parent {
private Long id;
...
}
public class SubClassA extends Parent {
private String stringA;
private Integer intA;
...
}
public class SubClassB extends Parent {
private String stringB;
private Integer intB;
...
}
Deserialize JSON with Jackson into Polymorphic Types - A Complete Example is giving me a compile error
As promised, I'm putting an example for how to use annotations to serialize/deserialize polymorphic objects, I based this example in the Animal
class from the tutorial you were reading.
First of all your Animal
class with the Json Annotations for the subclasses.
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "Dog"),
@JsonSubTypes.Type(value = Cat.class, name = "Cat") }
)
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then your subclasses, Dog
and Cat
.
public class Dog extends Animal {
private String breed;
public Dog() {
}
public Dog(String name, String breed) {
setName(name);
setBreed(breed);
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
}
public class Cat extends Animal {
public String getFavoriteToy() {
return favoriteToy;
}
public Cat() {}
public Cat(String name, String favoriteToy) {
setName(name);
setFavoriteToy(favoriteToy);
}
public void setFavoriteToy(String favoriteToy) {
this.favoriteToy = favoriteToy;
}
private String favoriteToy;
}
As you can see, there is nothing special for Cat
and Dog
, the only one that know about them is the abstract
class Animal
, so when deserializing, you'll target to Animal
and the ObjectMapper
will return the actual instance as you can see in the following test:
public class Test {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Animal myDog = new Dog("ruffus","english shepherd");
Animal myCat = new Cat("goya", "mice");
try {
String dogJson = objectMapper.writeValueAsString(myDog);
System.out.println(dogJson);
Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);
System.out.println("Deserialized dogJson Class: " + deserializedDog.getClass().getSimpleName());
String catJson = objectMapper.writeValueAsString(myCat);
Animal deseriliazedCat = objectMapper.readValue(catJson, Animal.class);
System.out.println("Deserialized catJson Class: " + deseriliazedCat.getClass().getSimpleName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Output after running the Test
class:
{"@type":"Dog","name":"ruffus","breed":"english shepherd"}
Deserialized dogJson Class: Dog
{"@type":"Cat","name":"goya","favoriteToy":"mice"}
Deserialized catJson Class: Cat
Hope this helps,
Jose Luis
jackson polymorphic deserialization with a custom criteria
If you can figure out a way to determine the object subtype from the JSON data, you could write a custom TypeId resolver to handle it for you. See Jackson Custom TypeId Resolver.
Deserialize node to subtype based on root name with Jackson
At the Github page of Jackson someone asked for a solution for the the same problem as what you are facing, see here: https://github.com/FasterXML/jackson-databind/issues/1627
A work around for the time-being would be a custom deserializer where a field within the class should contain what type of class it is so it can be properly deserialized. See here for the work around: https://stackoverflow.com/a/50013090/6777695
Related Topics
Is the Buildsessionfactory() Configuration Method Deprecated in Hibernate
What Does It Mean to Program to an Interface
Should I Use Java's String.Format() If Performance Is Important
Retrieve Version from Maven Pom.Xml in Code
Ignoring Ssl Certificate in Apache Httpclient 4.3
Why Are Data Transfer Objects (Dtos) an Anti-Pattern
Convert Java.Time.Localdate into Java.Util.Date Type
Naming Threads and Thread-Pools of Executorservice
Jpa: How to Have One-To-Many Relation of the Same Entity Type
Good Reasons to Prohibit Inheritance in Java
@Aspectj Pointcut for All Methods of a Class with Specific Annotation
Get the Indices of an Array After Sorting
How Can a Class Have a Member of Its Own Type, Isn't This Infinite Recursion
How to Create an Instance of an Interface in Java