Skip to content

Cannot deserialize abstract class with type parameter #435

Open
@alexblickenstaff

Description

@alexblickenstaff

I'm trying to serialize and deserialize an abstract class that has a type parameter, but the deserialization isn't working. Instead of deserializing the JSON into an object of the type parameter's class, Jackson deserializes it into a Map, so I'm unable to read the object's properties.

Here is the code:

import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo}
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule

object GithubExample {

  @JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@type")
  @JsonSubTypes(Array(
    new JsonSubTypes.Type(value = classOf[ResultWrapperSuccess[_]]),
    new JsonSubTypes.Type(value = classOf[ResultWrapperFailure[_]])
  ))
  trait ResultWrapperInterface[T] {
    protected def obj: T
  }

  case class ResultWrapperSuccess[T](result: T) extends ResultWrapperInterface[T] {
    override protected def obj: T = result
  }

  case class ResultWrapperFailure[F](failure: F) extends ResultWrapperInterface[F] {
    override protected def obj: F = failure
  }

  case class User(name: String, age: Option[Int])

  def main(args: Array[String]): Unit = {

    val mapper = new ObjectMapper()
    mapper.registerModule(DefaultScalaModule)

    val user = User("John Smith", Some(39))
    val serializedUser = mapper.writeValueAsString(user)
    println(s"(1)        serializedUser: $serializedUser")

    val deserializedUser = mapper.readValue(serializedUser, classOf[User])
    println(s"(2)      deserializedUser: $deserializedUser")
    println(s"(3) deserializedUser.name: ${deserializedUser.name}")


    val wrapperSuccess = ResultWrapperSuccess[User](user)
    val serializedSuccess = mapper.writeValueAsString(wrapperSuccess)
    println(s"(4)     serializedSuccess: $serializedSuccess")

    val deserializedSuccess = mapper.readValue(serializedSuccess, classOf[ResultWrapperInterface[User]])
    deserializedSuccess match {
      case _: ResultWrapperFailure[_] =>
      case success: ResultWrapperSuccess[User] =>
        println(s"(5)               success: $success")
        println(s"(6)        success.result: ${success.result}")
        println(s"(7)   success.result.name: ${success.result.name}")
    }

  }

}

The first part when we serialize and deserialize the User object works just fine. The code breaks on (7) when it tries to access success.result.name because success.result is somehow a Map instead of a User.

Here is the output:

(1)        serializedUser: {"name":"John Smith","age":39}
(2)      deserializedUser: User(John Smith,Some(39))
(3) deserializedUser.name: John Smith
(4)     serializedSuccess: {"@type":"GithubExample$ResultWrapperSuccess","result":{"name":"John Smith","age":39}}
(5)               success: ResultWrapperSuccess(Map(name -> John Smith, age -> 39))
(6)        success.result: Map(name -> John Smith, age -> 39)
Exception in thread "main" java.lang.ClassCastException: scala.collection.immutable.Map$Map2 cannot be cast to GithubExample$User
    at GithubExample$.main(GithubExample.scala:55)
    at GithubExample.main(GithubExample.scala)

As evidenced by the logs, the serialization seems to be working just fine. Is there something I need to change to get the deserialization working?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions