You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala泛型方法转换JsonNode时Int类型转换失效问题求解

问题根因

你遇到的问题不是Scala match case语法本身的问题,是三个因素共同作用导致的:

  • 泛型类型擦除后,你用ClassTag做类型匹配时,classOf[Int]对应Scala的原生值类型Int,但你把转换后的值放到无类型的match分支里时,Int会自动装箱为Java的java.lang.Integer,和Scala的Int类型匹配失败
  • 你统一对数值节点调用asDouble再转目标类型的逻辑,本身也会丢失原始数值的类型信息,增加不必要的装箱拆箱开销
  • 最后的case v:T类型检查因为泛型擦除,实际是基于ClassTag的运行时检查,装箱后的Java数值类型和Scala原生值类型不匹配,就会全部返回None

修复后代码

import com.fasterxml.jackson.databind.JsonNode
import scala.jdk.CollectionConverters._
import scala.reflect.{ClassTag, classTag}

implicit class JsonNodeToScala(jsonNode: JsonNode) {

  def toSeq[T: ClassTag]: Seq[Option[T]] = 
    jsonNode.elements.asScala.map(_.toScala[T]).toSeq

  // 如果需要直接返回无Option的序列(自动忽略null值),可以用下面这个方法
  def toSeqWithoutNone[T: ClassTag]: Seq[T] = 
    jsonNode.elements.asScala.flatMap(_.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] = {
    val targetCls = classTag[T].runtimeClass
    val rawResult = jsonNode match {
      case v if v.isNull => None
      case v if v.isBoolean => Some(v.asBoolean())
      case v if v.isNumber =>
        targetCls match {
          case c if c == classOf[Int] || c == classOf[java.lang.Integer] => Some(v.asInt())
          case c if c == classOf[Long] || c == classOf[java.lang.Long] => Some(v.asLong())
          case c if c == classOf[Double] || c == classOf[java.lang.Double] => Some(v.asDouble())
          case c if c == classOf[Float] || c == classOf[java.lang.Float] => Some(v.asFloat())
          case _ => Some(v.numberValue())
        }
      case v if v.isTextual => Some(v.asText())
      case _ => None
    }

    rawResult.flatMap { value =>
      // 直接匹配成功的场景
      if (targetCls.isInstance(value)) Some(value.asInstanceOf[T])
      // 兼容Scala原生值类型和Java装箱类型的映射
      else (targetCls, value) match {
        case (c, v: java.lang.Number) if c == classOf[Int] => Some(v.intValue().asInstanceOf[T])
        case (c, v: java.lang.Number) if c == classOf[Long] => Some(v.longValue().asInstanceOf[T])
        case (c, v: java.lang.Number) if c == classOf[Double] => Some(v.doubleValue().asInstanceOf[T])
        case (c, v: java.lang.Number) if c == classOf[Float] => Some(v.floatValue().asInstanceOf[T])
        case _ => None
      }
    }
  }
}

效果验证

你要求的所有场景都可以正常返回结果:

  • new ObjectMapper().readTree("[1, 2, 3]").toSeqWithoutNone[Int] => Seq(1, 2, 3)
  • new ObjectMapper().readTree("[1, 2, 3]").toSeqWithoutNone[Double] => Seq(1.0, 2.0, 3.0)
  • new ObjectMapper().readTree("[1.0, 2.0, 3.0]").toSeqWithoutNone[Int] => Seq(1, 2, 3)
  • new ObjectMapper().readTree("[1.0, 2.0, 3.0]").toSeqWithoutNone[Double] => Seq(1.0, 2.0, 3.0)

内容的提问来源于stack exchange,提问作者Lotcher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 20:33:03