Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Did I just break JVM type safety by using Jackson JSON deserialisation?

This is what I got:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModuleobject 

AppStart extends App {
  val mapper = new ObjectMapper()
  mapper.registerModule(DefaultScalaModule)

  val json = """{"id":"AB","stuff":"whatever"}"""
  val obj = mapper.readValue(json, classOf[TestClass])

  println(obj.id.get) // prints AB !!!
}

case class TestClass(id: Option[Int] = None, stuff: Option[String] = None)

At the same time, this does not even build:

val bad: Option[Int] = "AB"

There is clearly something wrong here. Versions that I am using in project:

scalaVersion := "2.11.6"

libraryDependencies += "com.fasterxml.jackson.module" % "jackson-module-scala_2.11" % "2.7.3"

like image 612
brunedito Avatar asked Oct 31 '22 03:10

brunedito


1 Answers

No, this doesn't break JVM type safety. JVM doesn't support generics, so far as it's concerned the type of id is Option, not Option[Int]. To break type safety, you'd have to get a TestClass whose id is not an Option.

Reflection and casting certainly break Java's and Scala's type safety, and they are supposed to.

To deserialize generics properly in Jackson, you need to supply additional information (see http://wiki.fasterxml.com/JacksonFAQ#Deserializing_Generic_types) and jackson-module-scala allows Scala compiler to supply it:

You can also mixin ScalaObjectMapper (experimental) to get rich wrappers that automatically convert scala manifests directly into TypeReferences for Jackson to use:

val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
val myMap = mapper.readValue[Map[String,Tuple2[Int,Int]]](src)

I don't know if it'll automatically prevent the issue you have as well.

like image 171
Alexey Romanov Avatar answered Nov 08 '22 12:11

Alexey Romanov