我想实现一种将 JSON 数据转换为 Scala 内置对象的方法。该代码类似于以下内容:
implicit class JsonNodeToScala(jsonNode: JsonNode) {
import scala.jdk.CollectionConverters._
import scala.reflect.{ClassTag,classTag}
def toSeq[T: ClassTag]: Seq[Option[T]] = jsonNode.elements.asScala.map(_.toScala[T]).toSeq
def toMap[T: ClassTag]: Map[String, Option[T]] = jsonNode.fields.asScala.map(field => (field.getKey, field.getValue.toScala[T])).toMap
def toScala[T: ClassTag]: Option[T] = (this.jsonNode match {
case v if v.isBoolean => v.asBoolean
case v if v.isNumber =>
(v.asDouble, classTag.runtimeClass) match {
case (v, c) if c == classOf[Int] =>
print("toInt")
v.toInt
case (v, c) if c == classOf[Long] =>
print("toLong")
v.toLong
case (v, _) => v
}
case v if v.isTextual => v.asText
case jsonNode => jsonNode
}) match {
case v: T => Some(v)
case v =>
println(v, classTag.runtimeClass, v.getClass)
None
}
}
当我使用它时它工作如下
new ObjectMapper().readTree("""[1,2,null,4]""").toSeq[Double]
>>> res136: Seq[Option[Double]] = List(
Some(value = 1.0),
Some(value = 2.0),
None,
Some(value = 4.0)
)
但是当我传入泛型参数int时,却得不到我想要的结果。
new ObjectMapper().readTree("""[1,2,null,4]""").toSeq[Int]
>>>
toInt(1.0,int,class java.lang.Double)
toInt(2.0,int,class java.lang.Double)
(null,int,class com.fasterxml.jackson.databind.node.NullNode)
toInt(4.0,int,class java.lang.Double)
res138: Seq[Option[Int]] = List(None, None, None, None)
结果调用了toint,但是v的类型还是double。是不是 Scala 在 match case 中的处理造成的?
如果我想满足我的需求,我应该如何修改我的代码?
"[1, 2, 3]" call toSeq[Int] => Seq(1, 2, 3)
"[1, 2, 3]" call toSeq[Double] => Seq(1.0, 2.0, 3.0)
"[1.0, 2.0, 3.0]" call toSeq[Int] => Seq(1, 2, 3)
"[1.0, 2.0, 3.0]" call toSeq[Double] => Seq(1.0, 2.0, 3.0)
这里的主要问题是:
case v if v.isNumber =>
(v.asDouble, classTag.runtimeClass) match {
case (v, c) if c == classOf[Int] =>
print("toInt")
v.toInt
case (v, c) if c == classOf[Long] =>
print("toLong")
v.toLong
case (v, _) => v
}
Scala 编译器必须找到Int
,Long
和之间的公共最低祖先Double
。在 Scala 2.12 中,它恰好是Double
,所以它再次转换Int
为Double
(通过隐式转换)。一种欺骗编译器的方法应该是向上转换一些返回类型,这样它就不能在数字类型之间执行隐式类型转换。例如:
case v if v.isNumber =>
(v.asDouble, classTag.runtimeClass) match {
case (v, c) if c == classOf[Int] =>
print("toInt")
v.toInt
case (v, c) if c == classOf[Long] =>
print("toLong")
v.toLong
case (v, _) => (v : Any) // common lowest type now is Any, no conversions will be performed
}
我希望我能帮助你:)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句