We all know that generic types are subject to type erasure under Java and Scala. But we ran into a strange problem in Scala using Jackson and the Scala Jackson Module.
我们都知道泛型类型在Java和Scala下受类型擦除的影响。但是我们使用Jackson和Scala Jackson Module在Scala中遇到了一个奇怪的问题。
I created a small test to show the problem.
我创建了一个小测试来显示问题。
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)
}
}
The output of the above is:
以上的输出是:
Test 1: 6
Test 2: java.lang.String cannot be cast to java.lang.Integer
Test 3: 6
Test 4: 6
Where does this different kind of behaviour come from? Generic Type Erasure, Jackson, Jackson Scala Module?
这种不同的行为来自哪里? Generic Type Erasure,Jackson,Jackson Scala Module?
2 个解决方案
#1
4
This is becoming such a common question that I wrote a FAQ for it:
这是一个常见的问题,我为它写了一个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
.[A] ll基本类型参数表示为JVM的Object。 ... Scala模块告诉Jackson,Option实际上是一个容器类型,但它依赖于Java反射来确定包含的类型,并提出了Object。
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:此用例的当前解决方法是将@JsonDeserialize注释添加到目标成员。具体来说,此注释具有一组可用于不同情况的参数:
contentAs
for collections or map values (supported)- contentAs for collections或map values(支持)
keyAs
for Map keys (currently unsupported)- 地图键的keyAs(当前不受支持)
Examples of how to use this annotation can be found in the tests directory.
可以在tests目录中找到如何使用此批注的示例。
There's a lot more detail in the FAQ for the curious.
好奇的常见问题解答中有更多细节。
#2
0
It seems to be because java.lang.Integer
has a constructor which takes a String, which allows the deserializer to use that for Integer
whereas that is not the case with Int
这似乎是因为java.lang.Integer有一个构造函数,它接受一个String,它允许反序列化器将其用于Integer,而Int则不是这样。
#1
4
This is becoming such a common question that I wrote a FAQ for it:
这是一个常见的问题,我为它写了一个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
.[A] ll基本类型参数表示为JVM的Object。 ... Scala模块告诉Jackson,Option实际上是一个容器类型,但它依赖于Java反射来确定包含的类型,并提出了Object。
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:此用例的当前解决方法是将@JsonDeserialize注释添加到目标成员。具体来说,此注释具有一组可用于不同情况的参数:
contentAs
for collections or map values (supported)- contentAs for collections或map values(支持)
keyAs
for Map keys (currently unsupported)- 地图键的keyAs(当前不受支持)
Examples of how to use this annotation can be found in the tests directory.
可以在tests目录中找到如何使用此批注的示例。
There's a lot more detail in the FAQ for the curious.
好奇的常见问题解答中有更多细节。
#2
0
It seems to be because java.lang.Integer
has a constructor which takes a String, which allows the deserializer to use that for Integer
whereas that is not the case with Int
这似乎是因为java.lang.Integer有一个构造函数,它接受一个String,它允许反序列化器将其用于Integer,而Int则不是这样。