Polymorphism in jackson annotations: @JsonTypeInfo usage
Asked Answered
R

3

59

I would like to know if @JsonTypeInfo annotation can be used for interfaces. I have set of classes which should be serialized and deserialized.

Here is what I'm trying to do. I have two implementation classes Sub1, Sub2 implementing MyInt. Some of the model classes have the interface reference for the implementation types. I would like to deserialize the objects based on polymorphism

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.WRAPPER_OBJECT)
@JsonSubTypes({
    @Type(name="sub1", value=Sub1.class), 
    @Type(name="sub2", value=Sub2.class)})
public interface MyInt{
}

@JsonTypeName("sub1")
public Sub1 implements MyInt{
}

@JsonTypeName("sub2")
public Sub2 implements MyInt{
}

I get the following JsonMappingException:

Unexpected token (END_OBJECT), expected FIELD_NAME: need JSON String that contains type id

Ricciardi answered 3/8, 2012 at 15:9 Comment(2)
you saw this? jackson-users.ning.com/forum/topics/… do you have any list without type mentioned like discussed in the other thread?Vogler
I think you need to show bit more code (how serialization, deserialization is done), as well as JSON being produced/consumed.Exonerate
V
54

@JsonSubTypes.Type must have a value and a name like this,

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.WRAPPER_OBJECT, property="type")
@JsonSubTypes({       
    @JsonSubTypes.Type(value=Dog.class, name="dog"),
    @JsonSubTypes.Type(value=Cat.class, name="cat")       
}) 

In the subclass, use @JsonTypeName("dog") to say the name.
The values dog and cat will be set in the property named type.

Vogler answered 3/8, 2012 at 15:14 Comment(13)
Thanks for the response. However, I still get the same error. Do you think I should change anything in @JsonTypeInfo also ?Ricciardi
Does any one know if JsonTypeInfo can be applied to interfaces at all ?Ricciardi
Issue could be with your input json string. are you trying to POST something?Vogler
No. I'm just doing a HTTP-GETRicciardi
ok, please update your question to include the subclasses also. what are the annotations on your subclasses?Vogler
updated the answer, add property in the @JSONTypeInfo and see if it works, sorry i didnt notice that earlier.Vogler
And yes, @JsonTypeInfo can be used for interfaces, if that helps (annotation handling by Jackson does support inheritance)Exonerate
@SenthilKumar Thanks for the info.. I was missing property="type"Ricciardi
@Exonerate Thanks for the info.. This annotation is applicable to interfaces as well.Ricciardi
Can I do it without annotations?Judd
But what if someone else is trying to implement your Interface? You cannot unmarshal a new concrete type named "sub3" to a MyInt without adding the annotation in your Interface file.Vedanta
Good explanation and updated examples here: davismol.net/2015/03/05/…Clance
This worked for me for Interfaces.. well Appreciated explanationPasteup
B
6

Yes it can be used both for abstract classes and interfaces.

Consider following code example

Suppose we have an enum , interface and classes

enum VehicleType {
    CAR,
    PLANE
}

interface Vehicle {
    VehicleType getVehicleType();
    String getName();
}


@NoArgsConstructor
@Getter
@Setter
class Car implements Vehicle {
    private boolean sunRoof;
    private String name;

    @Override
    public VehicleType getVehicleType() {
        return VehicleType.Car;
    }
}

@NoArgsConstructor
@Getter
@Setter
class Plane implements Vehicle {
    private double wingspan;
    private String name;

    @Override
    public VehicleType getVehicleType() {
        return VehicleType.Plane;
    }
}

If we try to deserialize this json into List<Vehicle>

[
  {"sunRoof":false,"name":"Ferrari","vehicleType":"CAR"}, 
  {"wingspan":19.25,"name":"Boeing 750","vehicleType":"PLANE"}
]

then we will get error

abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

To solve this just add following JsonSubTypes and JsonTypeInfo annotations to the interface, as shown below

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        property = "vehicleType")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Car.class, name = "CAR"),
        @JsonSubTypes.Type(value = Plane.class, name = "PLANE")
})
interface Vehicle {
    VehicleType getVehicleType();
    String getName();
}

With this the deserialization will work with interface and you will get a List<Vehicle> back

You can check out the code here - https://github.com/chatterjeesunit/java-playground/blob/master/src/main/java/com/play/util/jackson/PolymorphicDeserialization.java

Biogenesis answered 6/8, 2020 at 12:34 Comment(0)
D
0

For kotlin, it also works without declaring the Subtypes manually. Like this:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "kind")
sealed class Something {
    val kind = this::class.java.simpleName
}

Note you need to seal the class though.

Danner answered 2/6, 2022 at 16:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.