Play Framework Json 中的动态 Json 值

Dynamic Json values in Play Framework Json

我目前正在使用 Play 框架 json 解析器来解析我的 scala 代码中的 json 字符串。

我有以下 class:

case class Address(address: String,
                   gps: GPS,
                   country: String) {}

object Address {    
  implicit val reads: Reads[Address] = (
      (JsPath \ "address").read[String] and
      (JsPath \ "gps").read[GPS] and
      (JsPath \ "country").read[String]
    ) (Address.apply _)

  implicit val writes: Writes[Address] = (
      (JsPath \ "address").write[String] and
      (JsPath \ "gps").write[GPS] and
      (JsPath \ "country").write[String]
    ) (unlift(Address.unapply))
}

适用于以下 json:

{
    "address": "123 Fake Street",
    "country": "USA",
    "gps": { ... }
}

问题在于,在某些情况下,json 可能会将 gps 字段改为不解析的字符串,即

{
    "address": "123 Fake Street",
    "country": "USA",
    "gps": "123abc"
}

现在我知道我不能让 gps 成员既是字符串又是 GPS 对象,但是有没有办法让它成为 Option[GPS] 并且只有在 json 包含一个 gps 对象?

您的 impl 只需更改很少的部分。 您需要将字段 "gps" 读取为类似于 JsValue 'safe' 的内容,然后尝试将其映射到您的 GPS 案例中 class 如果可以,如果不能,return None.

case class GPS(a:String, b:String)
object GPS {
  val travelInfoReads = Json.reads[GPS]
  val travelInfoWrites = Json.writes[GPS]
  implicit val travelInfoFormat: Format[GPS] = Format(travelInfoReads, travelInfoWrites)
}
case class Address(address: String,
                   gps: Option[GPS],
                   country: String) {}

object Address {
  implicit val reads: Reads[Address] = (
    (JsPath \ "address").read[String] and
      (JsPath \ "gps").read[JsValue].map(js => js.asOpt[GPS]) and
      (JsPath \ "country").read[String]
    ) (Address.apply _)

  implicit val writes: Writes[Address] = (
    (JsPath \ "address").write[String] and
      (JsPath \ "gps").writeNullable[GPS] and
      (JsPath \ "country").write[String]
    ) (unlift(Address.unapply))
}

我也测试过:

val json = Json.toJson(Address("1",Some(GPS("a","b")),"2"))
      println(json)
      println(json.as[Address])
      val newObj: JsObject = (json.as[JsObject] - "gps") + ("gps" -> JsNumber(1))
      println(newObj)
      val a = newObj.as[Address]
      println(a)
      a must beEqualTo(Address("1",None,"2"))

输出就像

{"address":"1","gps":{"a":"a","b":"b"},"country":"2"}
Address(1,Some(GPS(a,b)),2)
{"address":"1","country":"2","gps":1}
Address(1,None,2)