1

I'm writing a kafka json deserializer in scala using Jackson but am having a problem providing jackson's readValue() method the class of a generic type. For example:

...
import org.apache.kafka.common.serialization.Deserializer

class JsonDeserializer[T] extends Deserializer[Option[T]] {

  val mapper = (new ObjectMapper() with ScalaObjectMapper)
    .registerModule(DefaultScalaModule)
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    .registerModule(new JavaTimeModule())
    .findAndRegisterModules()
    .asInstanceOf[ObjectMapper with ScalaObjectMapper]

  def deserialize(topic: String, bytes: Array[Byte]): Option[T] = {
    Option(bytes) match {
      case Some(b) => Some(mapper.readValue(bytes, classOf[T]))
      case None => None
    }

  }

  def configure(configs: java.util.Map[String, _], isKey: Boolean) {}
  def close(): Unit = {}
}

Notice the mapper.readValue(bytes, classOf[T]) in the deserialize method. Compilation fails with "class type required but T found".

How can this be done?

novon
  • 973
  • 3
  • 15
  • 30

1 Answers1

4

Generic types in Java are erased at runtime so there's no way to recover the Class without passing it in explicitly.

Well, explicitly as far as Java is concerned. You can use an (implicit ct: ClassTag[T]) or the shorthand [T: ClassTag] to (implicitly) obtain a ClassTag at construction time, which allows you to retrieve Class later on.

import scala.reflect._

class JsonDeserializer[T: ClassTag] extends Deserializer[Option[T]] {
  ...
    mapper.readValue(bytes, classTag[T].runtimeClass.asInstanceOf[Class[T]])
ephemient
  • 198,619
  • 38
  • 280
  • 391
  • I tried that actually but the context bound `[T: ClassTag]` seems to automatically change the primary constructor's no-arg signature which makes Kafka unable to instantiate it due to: *Could not instantiate class com.me.serialization.JsonDeserializer Does it have a public no-argument constructor?* – novon Jul 21 '17 at 06:42
  • @novon Unfortunately, it's not possible. For any particular type `Foo` you can easily create a `class OptFooDeserializer extends JsonDeserializer[Foo]` and use `OptFooDeserializer` though. – ephemient Jul 21 '17 at 06:47