5

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"

brunedito
  • 53
  • 3
  • Followed answer from @AlexeyRomanov that explains why this is happening. Decided to go with @JsonDeserialize annotation on type members in question, in this case: `case class TestClass(@JsonDeserialize(contentAs = classOf[Int]) id: Option[Int] = None, stuff: Option[String] = None)` ... which then throws Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of int from String value ("AB"): not a valid Integer value – brunedito May 17 '16 at 08:13

2 Answers2

3

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.

Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
-1

You would need to look at the byte code of the class Scala generates.

My guess is that reflection is still working, however the runtime type of the field is not what it appears in Scala, so the JVM doesn't prevent this happening.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130