Deserializing Polymorphic Types with Jackson Based on the Presence of a Unique Property

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



Leave a reply



Submit