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

时间:2017-02-10 18:37:11

标签: json scala playframework-2.5

我有一些来自外部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类:

case class Room(area: Double)

我使用来自play.api.libs.json的Read来将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)

1 个答案:

答案 0 :(得分:1)

一个棘手的小挑战,但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's Law方式为您自己的消费者进行清理。

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,它被定义为{ {1}}。

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

一旦您以数字方式对房间进行排序,我们就可以sortBy对它们进行排序,并使用我们的map转换器对每个房间进行转换。

您可能已经注意到我的自定义roomReads实施绝对是单行。这来自于处理古怪的上游JSON格式的痛苦经历。有点冗长,当上游服务器突然改变其JSON格式时,使用更多的变量并且稍微分解一些东西可以节省大量时间!