我们都知道泛型类型在
Java和Scala下受类型擦除的影响.但是我们使用Jackson和Scala Jackson Module在Scala中遇到了一个奇怪的问题.
我创建了一个小测试来显示问题.
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule object GenericTest { case class TestWithInt(id: Option[Int]) case class TestWithInteger(id: Option[Integer]) def main(args: Array[String]) { val mapper = new ObjectMapper() mapper.registerModule(DefaultScalaModule) // Test with scala's Int val test = mapper.readValue[TestWithInt]("""{ "id" : 5 }""",classOf[TestWithInt]) print("Test 1: ") println(test.id.get + 1) val test2 = mapper.readValue[TestWithInt]("""{ "id" : "5" }""",classOf[TestWithInt]) print("Test 2: ") try { println(test2.id.get + 1) } catch { case e: ClassCastException => println(e.getMessage) } // Test with java.lang.Integer val test3 = mapper.readValue[TestWithInteger]("""{ "id" : 5 }""",classOf[TestWithInteger]) print("Test 3: ") println(test3.id.get + 1) val test4 = mapper.readValue[TestWithInteger]("""{ "id" : "5" }""",classOf[TestWithInteger]) print("Test 4: ") println(test4.id.get + 1) } }
以上的输出是:
Test 1: 6 Test 2: java.lang.String cannot be cast to java.lang.Integer Test 3: 6 Test 4: 6
这种不同的行为来自哪里? Generic Type Erasure,Jackson,Jackson Scala Module?
解决方法
这成为一个常见的问题,我为它写了一个
FAQ:
[A]ll primitive type parameters are represented as
Object
to the JVM. … The Scala module has informed Jackson thatOption
is effectively a container type,but it relies on Java reflection to determine the contained type,and comes up withObject
.The current workaround for this use case is to add the
@JsonDeserialize
annotation to the member being targeted. Specifically,this annotation has a set of parameters that can be used for different situations:
contentAs
for collections or map values (supported)keyAs
for Map keys (currently unsupported)Examples of how to use this annotation 07001.
好奇的常见问题解答中有更多细节.