Deserialize JSON with Jackson into Polymorphic Types - a Complete Example Is Giving Me a Compile Error

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

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;
...
}

Deserializing Polymorphic Types with @JsonUnwrapped using Jackson

My SinglePolyUnwrappedDeserializer from this Gist can handle a single polymorphic @JsonUnwrapped property. It's in Kotlin, but can easily be ported to Java if needed. Example:

@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes(
JsonSubTypes.Type(value = A::class, name = "a"),
JsonSubTypes.Type(value = B::class, name = "b")
)
abstract class Base

data class A(val x: Int) : Base()

data class B(val y: Boolean) : Base()

@JsonDeserialize(using = SinglePolyUnwrappedDeserializer::class)
data class C(val a: String, @JsonUnwrapped val b: Base)

AFAIK, all combinations of other annotations are supported. The only limitation is that there is exactly one @JsonUnwrapped property.

If you also need a generic serializer for polymorphic @JsonUnwrapped, you can write it yourself very easily without any reflection or introspection: just merge the ObjectNode of the inner object onto the ObjectNode of the containing object.

Inheritance deserialisation with jackson in java

The answer has indeed been provided in the comments by @hollpolloi with the link: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

When you are using inheritence and Jackson you need a way to tell jackson how to differentiate different JSON from each other, and how to decide which object-type (class) each one is. The problem is that Jackson can get confused with similar JSON payloads when there is no attribute that tells jackson what class should be used.

Another good reference q/a is : Deserialize JSON with Jackson into Polymorphic Types - A Complete Example is giving me a compile error

A very simple solution is to annotate your classes with something like the following:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Animal {
...
}

See also:

  • https://www.logicbig.com/tutorials/misc/jackson/jackson-json-type-info-annotation.html

Jackson Polymorphic Deserialization based on Enum

Fixed!

It works with jackson 2.0!!

How does Jackson deserialize json into a generic type?

Your problem is here:

new TypeReference<T>()

This doesn't do what you expect it to do. Java generics are erased at runtime; therefore the above statement is basically new TypeReference<Object>.

In other words - the fact that you declared

Person<Child> readPerson = new Person<>();

to expect Child objects is simply not sufficient!

You probably have to pass the specific class Child.class to the code that maps JSON strings back. For further information, look here.



Related Topics



Leave a reply



Submit