-
-
Notifications
You must be signed in to change notification settings - Fork 143
Description
Hi,
I am using "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.10.0"
I am using a deserializer to read json message post into kafka.
At a time we faced a java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long.
I dig into this issue and found the related issues in github and the FAQ https://github.com/FasterXML/jackson-module-scala/wiki/FAQ that indicate to annotate offending field with @JsonDeserialize.
I tried this with no success.
My case class look like this (adapt like the one in the test case https://github.com/FasterXML/jackson-module-scala/blob/master/src/test/scala/com/fasterxml/jackson/module/scala/deser/PrimitiveContainerTest.scala)
case class AnnotatedOptionLongWithDash(@JsonDeserialize(contentAs = classOf[java.lang.Long]) `value-long`: Option[Long])
I try to understand why the test in scala module works but not our code and found that this is because the property contain a dash and we kept the dash in the scala props (Disgusting!).
I finally try to add @JsonProperty annotation to remove the dash and it's working.
I am not sure this issue is related to jackson deserialization and can be fix or it's scala/java that do something when a property have a dash in it.
At least it can help to add a note about this in the workarounds of the FAQ.
Here some tests case to reproduce the issue.
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.module.scala.{DefaultScalaModule, ScalaObjectMapper}
import org.scalatest.{Matchers, WordSpec}
case class AnnotatedOptionLong(@JsonDeserialize(contentAs = classOf[java.lang.Long]) valueLong: Option[Long])
case class AnnotatedOptionLongWithDash(@JsonDeserialize(contentAs = classOf[java.lang.Long]) `value-long`: Option[Long])
case class AnnotatedOptionLongWithDashButChangeToCamelCase(@JsonProperty("value-long") @JsonDeserialize(contentAs = classOf[java.lang.Long]) valueLong: Option[Long])
class JacksonSerializationIssueTest extends WordSpec with Matchers {
val objectMapper = new ObjectMapper() with ScalaObjectMapper
objectMapper.registerModule(new DefaultScalaModule)
def deserialize[T](data: Array[Byte])(implicit m: Manifest[T]): T = {
try {
objectMapper.readValue(data)
} catch {
case e: Throwable =>
throw new RuntimeException("Error deserializing JSON message", e)
}
}
def serialize[T](data: T): Array[Byte] = {
try {
objectMapper.writeValueAsBytes(data)
} catch {
case e: Throwable =>
throw new RuntimeException("Error serializing JSON message", e)
}
}
def useOptionLong(v: Option[Long]): Long = v.map(_ * 2).getOrElse(0)
"same as in test source of jackon library" in {
// check deserialization
val v1 = deserialize[AnnotatedOptionLong]("""{"valueLong":151}""".getBytes)
v1 shouldBe AnnotatedOptionLong(Some(151L))
v1.valueLong.get shouldBe 151L
// serialize from case class then deserialize and then apply the method that will fail
val v2 = JacksonMapper.deserialize[AnnotatedOptionLong](JacksonMapper.serialize(AnnotatedOptionLong(Some(152))))
v2 shouldBe AnnotatedOptionLong(Some(152L))
v2.valueLong.get shouldBe 152L
useOptionLong(v2.valueLong) shouldBe 304
}
"failing test because of backtick prop name either if we apply the annotation @JsonDeserialize(contentAs = classOf[java.lang.Long]) " in {
// check deserialization
val v1 = deserialize[AnnotatedOptionLongWithDash]("""{"value-long":251}""".getBytes)
v1 shouldBe AnnotatedOptionLongWithDash(Some(251L))
v1.`value-long`.get shouldBe 251L
// serialize from case class then deserialize and then apply the method that will fail
val v2 = JacksonMapper.deserialize[AnnotatedOptionLongWithDash](JacksonMapper.serialize(AnnotatedOptionLongWithDash(Some(252))))
v2 shouldBe AnnotatedOptionLongWithDash(Some(252L))
v2.`value-long`.get shouldBe 252L
useOptionLong(v2.`value-long`) shouldBe 504
}
"working solution because we rename the prop with a dash to a camel case prop" in {
// check deserialization
val v1 = deserialize[AnnotatedOptionLongWithDashButChangeToCamelCase]("""{"value-long":351}""".getBytes)
v1 shouldBe AnnotatedOptionLongWithDashButChangeToCamelCase(Some(351L))
v1.valueLong.get shouldBe 351L
// serialize from case class then deserialize and then apply the method that will fail
val v2 = JacksonMapper.deserialize[AnnotatedOptionLongWithDashButChangeToCamelCase](JacksonMapper.serialize(AnnotatedOptionLongWithDashButChangeToCamelCase(Some(352))))
v2 shouldBe AnnotatedOptionLongWithDashButChangeToCamelCase(Some(352L))
v2.valueLong.get shouldBe 352L
useOptionLong(v2.valueLong) shouldBe 704
}
}
Initial question from google groups https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/jackson-user/NKSx88srl-g/X4Ea2PgfAwAJ