如何将PlayJSON列表转换为Scala Map(复杂)?

时间:2015-10-26 20:26:40

标签: python json scala playframework playframework-2.0

我正在将Python脚本转换为Scala,但我遇到了一些问题(请注意,我之前从未在Scala中编程)。

我正在向KairosDB发送API请求,我正在使用PlayJSON在Scala中解析它的响应。正如您所看到的in the Query Metrics Documentation,KairosDB使用如下所示的JSON进行响应:

{
  "queries": [
      {
          "sample_size": 14368,
          "results": [
              {
                  "name": "abc_123",
                  "group_by": [
                      {
                         "name": "type",
                         "type": "number"
                      },
                      {
                          "name": "tag",
                          "tags": [
                              "host"
                          ],
                          "group": {
                              "host": "server1"
                          }
                      }
                  ],
                  "tags": {
                      "host": [
                          "server1"
                      ],
                      "customer": [
                          "bar"
                      ]
                  },
                  "values": [
                      [
                          1364968800000,
                          11019
                      ],
                      [
                          1366351200000,
                          2843
                      ]
                  ]
              }
          ]
      }
  ]
}

目前我想解析这个响应(采用JSON格式)并创建如下结构(我想这将代表Scala Map):

{ 
    "abc_123": [
        [timestamp1, value1], 
        [timestamp2, value2]
     ],
     "abd_124": [
        [timestamp1, value1], 
        [timestamp2, value2]
     ]
}

换句话说,我想为“结果”中的每个传感器“名称”创建一个键,并将此键与此特定传感器“名称”的“值”数组相关联。我现在不需要保存/存储“sample_size”,“tags”和“group_by”,因为它对我来说没用。

目前我正在尝试解决这个问题(记得我正在使用PlayJSON):

// Get "results" from JSON
val parsedResponse = (((Json.parse(response.body.asString) \ "queries")(0)) \\ "results")

// Some commands that I've tested and works:
// Print keys: parsedResponse.foreach { p => print( (p(0)) \ "name" ) }
// Print values: parsedResponse.foreach { p => print((p(0) \\ "values")) }

// Here I'm trying to return a Set, but it don't work at all
// val data = parsedResponse.foreach { p => ((p(0)) \ "name").asOpt[String] -> ((p(0) \\ "values").asOpt[Seq]) }

请注意,我可以打印parsedResponse并获得以下内容:https://gist.github.com/paladini/b474bba6c3711ddcdacd

如何在Scala中实现这一目标?

1 个答案:

答案 0 :(得分:3)

与像python这样的动态语言相反,在Scala中,我们通常以类型安全的方式执行操作,包括json解析。我们通常不使用字典,我们总是将json转换为类型(并且更安全)。

在游戏中,您可以按照播放文档中的建议进行操作,使用play-json

结果会是这样的:

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

case class ResultValue(timestamp: Long, value: Long)
case class Result(name: String, values: Seq[ResultValue])

implicit val resultValueReads: Reads[ResultValue] = (
  (JsPath(0)).read[Long] and
  (JsPath(1)).read[Long]
)(ResultValue.apply _)

implicit val resultReads: Reads[Result] = (
  (JsPath \ "name").read[String] and
  (JsPath \ "values").read[Seq[ResultValue]]
)(Result.apply _)

val parsedResponse = (((Json.parse(text) \ "queries")(0)) \ "results").validate[Seq[Result]]

parsedResponse.asOpt match { 
  case Some(results) => println(results)
  case None => println("Nothing was parsed")
}

打印:

List(Result(abc_123,List(ResultValue(1364968800000,11019), ResultValue(1366351200000,2843))))

然后你可以像这样使用它:

results(0).name
results(0).values.timestamp
results(0).values.value

其他选择:json4s

play-json有点冗长(特别是因为你来自动态语言),所以如果你想要一种更简单的解析json的方法,你可以使用json4s。解决方案是这样的:

import org.json4s.DefaultFormats
import org.json4s.native.JsonMethods._

implicit val formats = DefaultFormats

case class Result(name: String, values: List[List[Long]])

val json = parse(text)

val results = ((json \ "queries")(0) \ "results").extract[List[Result]]

println(results(0).name)
println(results(0).values)

**使用play-json **的更简洁但不太安全的版本

case class Result(name: String, values: Seq[Seq[Long]])                                                                                                                

implicit val resultReads: Reads[Result] = (
  (JsPath \ "name").read[String] and
  (JsPath \ "values").read[Seq[Seq[Long]]]
)(Result.apply _)

val parsedResponse = (((Json.parse(text) \ "queries")(0)) \ "results").validate[Seq[Result]]

parsedResponse.asOpt match { 
  case Some(results) => println(results)
  case None => println("Nothing was parsed")
}

打印:

List(Result(abc_123,List(List(1364968800000, 11019), List(1366351200000, 2843))))