Java: Jackson polymorphic JSON deserialization of an object with an interface property?
You should use JsonTypeInfo.As.EXTERNAL_PROPERTY
instead of JsonTypeInfo.As.PROPERTY
. In this scenario your Asset
class should look like this:
class Asset {
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = ImageAssetProperties.class, name = "image"),
@JsonSubTypes.Type(value = DocumentAssetProperties.class, name = "document") })
private AssetProperties properties;
public AssetProperties getProperties() {
return properties;
}
public void setProperties(AssetProperties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Asset [properties("+properties.getClass().getSimpleName()+")=" + properties + "]";
}
}
See also my answer in this question: Jackson JsonTypeInfo.As.EXTERNAL_PROPERTY doesn't work as expected.
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
Deserializing json subtype based on parent property
You can achieve that by adding include = As.EXTERNAL_PROPERTY
to @JsonTypeInfo
. You just have to move the annotation to the field.
See the JavaDoc for EXTERNAL_PROPERTY:
Inclusion mechanism similar to
PROPERTY
, except that property is included one-level higher in hierarchy [...]
Here's an example:
@Data
class Field {
private String label;
private AttributeType attributeType;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.EXTERNAL_PROPERTY, property = "attributeType")
private Attribute attribute;
}
@Data
@JsonSubTypes({
@JsonSubTypes.Type(value = Attribute.NumberAttribute.class, name = "NUMBER"),
@JsonSubTypes.Type(value = Attribute.TextAttribute.class, name = "TEXT")
})
abstract class Attribute {
@Data
public static class TextAttribute extends Attribute {
List<Language> languages;
}
@Data
public static class NumberAttribute extends Attribute {
String value;
}
@Data
public static class Language {
private String text;
private String language;
}
}
enum AttributeType {
NUMBER, TEXT;
}
Related Topics
Noclassdeffounderror While Trying to Run My Jar with Java.Exe -Jar...What's Wrong
Localdate to Java.Util.Date and Vice Versa Simplest Conversion
Circular Dependency in Java Constructors
How to Remove a Substring from a Given String
How to Get Start and End Range from List of Timestamps
Java Server with Multiclient Communication
Sonar Violation: Security - Array Is Stored Directly
Deserializing an Abstract Class in Gson
Superclass Reference Not Able to Call Subclass Method in Java
Possible to Use Two Java Classes with Same Name and Same Package
Why Is Each Public Class in a Separate File
Building an Uberjar with Gradle
How to Get Which Jradiobutton Is Selected from a Buttongroup
How to Draw a Decent Looking Circle in Java
No Serializer Found for Class Org.Hibernate.Proxy.Pojo.Bytebuddy.Bytebuddyinterceptor