Search code examples
javajsonapache-kafkajacksonjson-deserialization

Kafka JSON Deserializer for interfaces


I've got problem similar to this: Kafka Deserialize Nested Generic Types In my kafka producer I am sending object that looks like this:

public class ExternalTO implements Serializable
{
    private static final long serialVersionUID = 7949808917892350503L;
    private List<IExternalData> externalDatas;

    public ExternalTO()
    {}
}

The cornerstone is this: List<IExternalData> externalDatas.

This interface looks like:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public interface IExternalData
{
    String getOne();
}

In my application there can by generated multiple types of IExternalBetData interface implementations (about 10 different). In this case, for instance, my producer generated ExternalTO with inner list of ConcreteExternalData objects. Sent JSON looks like:

{
"externalDatas":
[{"@class":"com.api.external.to.ConcreteExternalData",
"one":false,
"two":false}]
}

Field @class was added because of @JsonTypeInfo annotation, and I thought that this is enough for deserializer to "understend" what type of IExternalData to use in deserialization. Unfortunately, on the side of kafka listener I am getting the exception:

Cannot construct instance of com.api.external.to.IExternalData (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

Consumer looks similar to:

@Service
public class Consumer
{
    private final ObjectMapper objectMapper;


    public Consumer(ObjectMapper objectMapper)
    {
        this.objectMapper = objectMapper;
    }


    @KafkaListener(topics = {"${kafka.topic}"})
    public void listen(ConsumerRecord<String, String> record)
    {
        objectMapper.readValue(record.value(), ExternalTO.class)
    }

Please, help to solve this issue with deseriatization.


Solution

  • The solution for me was to set property to objectMapper.

    ObjectMapper mapper = new ObjectMapper();
    // deserializes IExternalData into certain implementation.
    mapper.enableDefaultTyping();