How to add custom deserializer to interface using jackson

14,375

Solution 1

It seems you forgot to annotate your implementation classes with @JsonDeserialize(using = ImplementationClazz.class) to indicate that the class should be used to deserialize the abstract class or interface.

The following is a simple example to deserialize an interface having multiple implementations using Jackson.

Here is my interface:

@JsonDeserialize(using = UserDeserializer.class)
public interface User {
}

One implementation of the interface:

@JsonDeserialize(as = ServiceUser.class)
public class ServiceUser implements User{

    private String name;
    private String role;

    //constructor, setters & getters

Second implementation:

@JsonDeserialize(as = AdminUser.class)
public class AdminUser implements User {

    private String role;
    private String designation;

    //constructor, setters & getters

And here is the deserializer:

public class UserDeserializer extends JsonDeserializer<User> {
    @Override
    public User deserialize(JsonParser jp, DeserializationContext context) throws IOException {
        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        ObjectNode root = mapper.readTree(jp);

        /*write your own condition*/
        if (root.has("name") && root.get("name").asText().equals("XYZ")) {
            return mapper.readValue(root.toString(), ServiceUser.class);
        }
        return mapper.readValue(root.toString(), AdminUser.class);
    }
}

You may get a StackOverflowError if you don't annotate the implementation classes. All implementation classes should deserialize themselves, otherwise it will use the deserializer from the parent class which leads to a StackOverflowError.

Solution 2

Just in case someone need a solution to serialize and desiralize inheritance hierarchy

you can use jackson annotation in more elegant way : JsonTypeInfo and JsonSubTypes

 @JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME, 
  include = JsonTypeInfo.As.PROPERTY, 
  property = "type")
@JsonSubTypes({ 
  @Type(value = ServiceUser.class, name = "service"), 
  @Type(value = AdminUser.class, name = "admin") 
})
public interface User{
    // ...
}
Share:
14,375
dustdn
Author by

dustdn

a confused student

Updated on June 05, 2022

Comments

  • dustdn
    dustdn almost 2 years

    Saying I have an interface A, I want to use custom deserializer for all classes implement interface A, So I use code below but it doesn't work, While CustomAserializer works. So what should I do to deserialize all classes implement A using my custom deserializer. Thanks.

    module.addDeserializer(A.class, new CustomADeserializer());
    module.addSerializer(A.class, new CustomASerializer())
    
  • asgs
    asgs over 6 years
    Perfect! this is what I was looking for.
  • Partha
    Partha over 5 years
    This does the job for simple cases, but if you have a nested list of items that can contain various subclasses, this doesn't work. For that, you have to use the JsonCreator along with the above solution.