Play 2.2 JSON Reads with combinators:如何处理嵌套的可选对象?

Posted

技术标签:

【中文标题】Play 2.2 JSON Reads with combinators:如何处理嵌套的可选对象?【英文标题】:Play 2.2 JSON Reads with combinators: how to deal with nested optional objects? 【发布时间】:2013-10-29 01:57:01 【问题描述】:

在 Play Framework 2.2 中解析这个 JSON 结构我快疯了:

val jsonStr = """ personFirstName: "FirstName",
  personLastName: "LastName"
  positionLat: null,
  positionLon: null """

我有 2 个案例类:

case class Position( val lat: Double, val lon: Double)
case class Person( firstName: String, lastName: String, p: Option[Position] )

如您所见,位置在 Person 案例类中不是强制性的。

我试图使用类似的方法获取 Person 的实例

implicit val reader = (
  (__ \ 'personFirstName ).read[String] ~
  (__ \ 'personLastName ).read[String] ~
  ( (__ \ 'positionLat ).read[Double] ~
    (__ \ 'positionLon ).read[Double] )(Position)
)(Person)

但我很快意识到我不知道如何处理 Option[Position] 对象:如果指定了“lat”和“lon”而不是 null,则意图是实例化 Some(Position(lat,lon)),否则实例化 @987654326 @。

你会怎么处理?

【问题讨论】:

【参考方案1】:

我很确定有比我要发布的内容更好的方法来做你想做的事,但已经晚了,我现在想不通。我假设这里不能简单地更改您正在使用的 JSON 结构。

您可以提供一个构建器函数,该函数接受两个 可选 lat/lon 双精度值,如果它们都存在则产生一个位置。

import play.api.libs.functional.syntax._
import play.api.libs.json._

val jsonStr = """
  "personFirstName": "FirstName",
  "personLastName": "LastName",
  "positionLat": null,
  "positionLon": null """

case class Position(lat: Double, lon: Double)

case class Person( firstName: String, lastName: String, p: Option[Position] )

object Person 
  implicit val reader = (
    (__ \ "personFirstName" ).read[String] and
    (__ \ "personLastName" ).read[String] and (
      (__ \ "positionLat" ).readNullable[Double] and
      (__ \ "positionLon" ).readNullable[Double]
    )((latOpt: Option[Double], lonOpt: Option[Double]) => 
      for  lat <- latOpt ; lon <- lonOpt yield Position(lat, lon)
    )
  )(Person.apply _)


Json.parse(jsonStr).validate[Person] // yields JsSuccess(Person(FirstName,LastName,None),)

另外,请注意,要成为有效的 JSON,您需要 quote the data keys。

【讨论】:

【参考方案2】:

您的 javascript 对象应该与您的案例类的结构相匹配。 Position 还需要一个 json 阅读器。

val jsonStr = """ "personFirstName": "FirstName",
    "personLastName": "LastName",
    "position":
        "lat": null,
        "lon": null
     
"""

case class Person( firstName: String, lastName: String, p: Option[Position] )

object Person 

    implicit val reader = (
        (__ \ 'personFirstName ).read[String] ~
        (__ \ 'personLastName ).read[String] ~
        (__ \ 'position ).readNullable[Position]
    )(Person.apply _)



case class Position( val lat: Double, val lon: Double)

object Position 

    implicit val reader = (
        (__ \ 'lat ).read[Double] ~
        (__ \ 'lon ).read[Double]
    )(Position.apply _)


如果Position的任何一个字段在json对象中为空/缺失,它将被解析为None。所以,jsonStr.as[Person] = Person("FirstName", "LastName", None)

【讨论】:

感谢您的提示,但不幸的是我无法更改 JSON 结构,这就是为什么我不知道如何解决这个问题。

以上是关于Play 2.2 JSON Reads with combinators:如何处理嵌套的可选对象?的主要内容,如果未能解决你的问题,请参考以下文章

Play框架JSON读取:如何读取String或Int?

带有 Play 2.2 库的密封特征的无噪声 JSON 格式

将 Play 框架与 Scala 结合使用,如何将 Json Validation 消息人性化?

Play 2.2 动作未与 Web 服务响应处理程序并行处理

Question: Should I use reads with good quality but failed-vendor flag?--biostart for vendor quality

抽象类上的 Json.reads(不支持密封特征:没有已知的子类)