繁体   English   中英

如何在 Scala Play 框架 2 中将 JSON 字段转换为 Seq?

[英]How do I transform a JSON fields into a Seq in Scala Play framework 2?

我有一些来自外部 API 的 JSON,我无法控制。 部分 JSON 格式如下:

{
  "room_0": {
    "area_sq_ft": 151.2
  },
  "room_1": {
    "area_sq_ft": 200.0
  }
}

他们没有像他们应该使用的那样使用数组,而是使用 room_n 作为 n 个元素的键。 我不想使用 room_0、room_1、room_2 等创建案例类,而是想将其转换为 Seq[Room],其中这是我的 Room 案例类:

case class Room(area: Double)

我正在使用play.api.libs.json Reads 将 JSON 的其他部分转换为 case 类,并且更喜欢使用 Reads 进行此转换。 我怎么能做到这一点?

这是我尝试过的。

val sqFtReads = (__ \ "size_sq_ft").read[Double]
val roomReads = (__ \ "size_sq_ft").read[Seq[Room]](sqFtReads).map(Room) 
cmd19.sc:1: overloaded method value read with alternatives:
  (t: Seq[$sess.cmd17.Room])play.api.libs.json.Reads[Seq[$sess.cmd17.Room]] <and>
  (implicit r: play.api.libs.json.Reads[Seq[$sess.cmd17.Room]])play.api.libs.json.Reads[Seq[$sess.cmd17.Room]]
 cannot be applied to (play.api.libs.json.Reads[Double])
val roomReads = (__ \ "size_sq_ft").read[Seq[Room]](sqFtReads).map(Room)

一个棘手的小挑战,但完全可以通过Reads实现。

首先, Reads[Room] - 即单个Room实例的转换器:

val roomReads = new Reads[Room] {
  override def reads(json: JsValue): JsResult[Room] = {
    (json \ "area_sq_ft").validate[Double].map(Room(_))
  }
}

非常简单; 我们查看 JSON 并尝试找到一个名为area_sq_ft的顶级字段,该字段验证为Double 如果一切顺利,我们会根据需要返回填充的Room实例。

接下来,您的上游对象的转换器,以良好的Postel 定律方式,您正在为您自己的消费者进行清理。

val strangeObjectReads = new Reads[Seq[Room]] {
  override def reads(json: JsValue): JsResult[Seq[Room]] = {

    json.validate[JsObject].map { jso =>

      val roomsSortedNumerically = jso.fields.sortBy { case (name, contents) =>
        val numericPartOfRoomName = name.dropWhile(!_.isDigit)
        numericPartOfRoomName.toInt
      }

      roomsSortedNumerically.map { case (name, contents) =>
        contents.as[Room](roomReads)
      }

    }
  }
}

这里的关键是整个json.validate[JsObject] 通过map ping,我们得到了我们需要包装整个事物的JsResult ,此外,我们可以访问 JSON 对象中的fields ,该对象定义为Seq[(String, JsValue)]

为了确保我们在输出序列中以正确的顺序放置字段,我们进行了一些字符串操作,获取room_1字符串的数字部分,并将其用作sortBy条件。 我在这里有点天真,并假设您的上游服务器不会做任何诸如跳过房间号码之类的令人讨厌的事情!

一旦您按数字对房间进行了排序,我们就可以对它们进行map ,使用我们的roomReads转换器转换每个roomReads

您可能已经注意到,我的自定义Reads实现绝对不是单行的。 这来自处理奇怪的上游 JSON 格式的痛苦经验。 有点冗长,当上游服务器突然更改其 JSON 格式时,使用更多变量并稍微分解一下会带来很大的回报!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM