skip to Main Content

I’m trying to write a method that will allow Jackson ObjectMapper readValue on a json string to a parameterized object type. Something like this

case class MyObj(field1: String, field2: String)

val objectMapper: ObjectMapper = new ObjectMapper().registerModule(new DefaultScalaModule)

def fromJson[T](jsonString: String, objTyp: T): T = {

    objectMapper.readValue(jsonString, classOf[T])

}

val x = fromJson("""{"field1": "something", "field2": "something"}""", MyObj)

This of course returns an error of

class type required but T found

i’ve looked at this issue Scala classOf for type parameter
but it doesn’t seem to help. It seems like this is possible to do somehow. Looking for any help

2

Answers


  1. You have to give it the actual runtime class to parse into, not just a type parameter.

    One way to do it is passing the class directly:

    def fromJson[T](json: String, clazz: Class[T]) = objectMapper.readValue[T](json, clazz)
    
    val x = fromJson("""...""", classOf[MyObj])
    

    Alternatively, you can use ClassTag, which looks a bit messier in implementation, but kinda prettier at call site:

    def fromJson[T : ClassTag](json: String): T = objectMapper.readValue[T](
      json, 
      implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]]
    )
    
    val x = fromJson[MyObj]("""{"field1": "something", "field2": "something"}""")
    
    Login or Signup to reply.
  2. i’ve looked at this issue Scala classOf for type parameter but it doesn’t seem to help.

    In the very first answer there it’s written classTag[T].runtimeClass as a replacement of classOf[T]. This should help.

    Regarding the signature

    def fromJson[T](jsonString: String, objTyp: T): T
    

    You should notice that MyObj has type MyObj.type (companion-object type), not MyObj (case-class type).

    Class companion object vs. case class itself

    So if you call fromJson("""...""", MyObj) then the types in these two places

    def fromJson[...](jsonString: String, objTyp: ???): ???
                                                  ^^^   ^^^  <--- HERE
    

    can’t be the same.

    If it’s enough for you to call

    fromJson("""...""", classOf[MyObj])
    

    or

    fromJson[MyObj]("""...""")
    

    (normally it should be enough) then please see @Dima’s answer, you should prefer those options, they’re easier.

    Just in case, if you really want to call like fromJson("""...""", MyObj) then for example you can use the type class ToCompanion (this is more complicated) from

    Invoke construcotr based on passed parameter

    Get companion object of class by given generic type Scala (answer)

    // ToCompanion should be defined in a different subproject
    
    def fromJson[C, T](jsonString: String, objTyp: C)(implicit
      toCompanion: ToCompanion.Aux[C, T],
      classTag: ClassTag[T]
    ): T =
      objectMapper.readValue(jsonString, classTag.runtimeClass.asInstanceOf[Class[T]])
    
    val x = fromJson("""{"field1": "something", "field2": "something"}""", MyObj)
    // MyObj(something,something)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search