在 Json4s 中,为什么 JSON 对象中的整数字段会自动转换为字符串?

In Json4s why does an integer field in a JSON object get automatically converted to a String?

如果我有一个 JSON 对象,例如:

{
  "test": 3
}

然后我预计将 "test" 字段提取为字符串会失败,因为类型不一致:

import org.json4s._
import org.json4s.jackson.JsonMethods
import org.json4s.JsonAST.JValue

def getVal[T: Manifest](json: JValue, fieldName: String): Option[T] = {
  val field = json findField {
    case JField(name, _) if name == fieldName => true
    case _ => false
  }

  field.map {
    case (_, value) => value.extract[T]
  }
}

val json = JsonMethods.parse("""{"test":3}""")
val value: Option[String] = getVal[String](json, "test") // Was Some(3) but expected None

这是从 JSON 数字自动转换为 Json4s 中预期的字符串吗?如果是这样,是否有任何解决方法,其中提取的字段必须与 extract 方法的类型参数中指定的类型相同?

这是大多数(如果不是全部)解析器的默认特性。如果您请求类型 T 的值,并且该值可以安全地转换为该特定类型,那么该库将为您转换它。例如,看一下类型安全配置,它具有将数字字段转换为字符串的类似性质。

import com.typesafe.config._
val config = ConfigFactory parseString """{ test = 3 }"""
val res1 = config.getString("test")
res1: String = 3

如果您不想自动将 Integer/Boolean 转换为字符串,您可以像下面这样手动检查 Int/Boolean 类型。

if(Try(value.extract[Int]).isFailure || Try(value.extract[Boolean]).isFailure) {
    throw RuntimeException(s"not a String field. try Int or Boolean")
} else {
 value.extract[T]
}

一个简单的解决方法是为需要 "strict" 行为的情况创建自定义序列化程序。例如:

  import org.json4s._

  val stringSerializer = new CustomSerializer[String](_ => (
    {
      case JString(s) => s
      case JNull      => null
      case x          => throw new MappingException("Can't convert %s to String." format x)
    },
    {
      case s: String => JString(s)
    }
  ))

将此序列化程序添加到您的隐式格式可确保严格的行为:

  implicit val formats = DefaultFormats + stringSerializer

  val js = JInt(123)
  val str = js.extract[String] // throws MappingException