简体   繁体   English

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

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

I have some JSON coming from an external API which I have no control over.我有一些来自外部 API 的 JSON,我无法控制。 Part of the JSON is formatted like this:部分 JSON 格式如下:

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

Instead of using an array like they should have, they've used room_n for a key to n number of elements.他们没有像他们应该使用的那样使用数组,而是使用 room_n 作为 n 个元素的键。 Instead of creating a case class with room_0, room_1, room_2, etc., I want to convert this to a Seq[Room] where this is my Room case class:我不想使用 room_0、room_1、room_2 等创建案例类,而是想将其转换为 Seq[Room],其中这是我的 Room 案例类:

case class Room(area: Double)

I am using Reads from play.api.libs.json for converting other parts of the JSON to case classes and would prefer to use Reads for this conversion.我正在使用play.api.libs.json Reads 将 JSON 的其他部分转换为 case 类,并且更喜欢使用 Reads 进行此转换。 How could I accomplish that?我怎么能做到这一点?

Here's what I've tried.这是我尝试过的。

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)

A tricky little challenge but completely achievable with Reads .一个棘手的小挑战,但完全可以通过Reads实现。

First, Reads[Room] - ie the converter for a single Room instance:首先, Reads[Room] - 即单个Room实例的转换器:

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

Pretty straightforward;非常简单; we peek into the JSON and try to find a top-level field called area_sq_ft which validates as a Double .我们查看 JSON 并尝试找到一个名为area_sq_ft的顶级字段,该字段验证为Double If it's all good, we return the populated Room instance as needed.如果一切顺利,我们会根据需要返回填充的Room实例。

Next up, the converter for your upstream object that in good Postel's Law fashion, you are cleaning up for your own consumers.接下来,您的上游对象的转换器,以良好的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)
      }

    }
  }
}

The key thing here is the json.validate[JsObject] around the whole lot.这里的关键是整个json.validate[JsObject] By map ping over this we get the JsResult that we need to wrap the whole thing, plus, we can get access to the fields inside the JSON object, which is defined as a Seq[(String, JsValue)] .通过map ping,我们得到了我们需要包装整个事物的JsResult ,此外,我们可以访问 JSON 对象中的fields ,该对象定义为Seq[(String, JsValue)]

To ensure we put the fields in the correct order in the output sequence, we do a little bit of string manipulation, getting the numeric part of the room_1 string, and using that as the sortBy criteria.为了确保我们在输出序列中以正确的顺序放置字段,我们进行了一些字符串操作,获取room_1字符串的数字部分,并将其用作sortBy条件。 I'm being a bit naive here and assuming your upstream server won't do anything nasty like skip room numbers!我在这里有点天真,并假设您的上游服务器不会做任何诸如跳过房间号码之类的令人讨厌的事情!

Once you've got the rooms sorted numerically, we can just map over them, converting each one with our roomReads converter.一旦您按数字对房间进行了排序,我们就可以对它们进行map ,使用我们的roomReads转换器转换每个roomReads

You've probably noticed that my custom Reads implementations are most definitely not one-liners.您可能已经注意到,我的自定义Reads实现绝对不是单行的。 This comes from bitter experience dealing with oddball upstream JSON formats.这来自处理奇怪的上游 JSON 格式的痛苦经验。 Being a bit verbose, using a few more variables and breaking things up a bit pays off big time when that upstream server changes its JSON format suddenly!有点冗长,当上游服务器突然更改其 JSON 格式时,使用更多变量并稍微分解一下会带来很大的回报!

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

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