Spray-Json:如何解析 Json 数组?

Posted

技术标签:

【中文标题】Spray-Json:如何解析 Json 数组?【英文标题】:Spray-Json: How to parse a Json Array? 【发布时间】:2013-12-29 16:21:04 【问题描述】:

我是 Spray-Json API 的新手,我正在尝试解析来自 Docker REST API 的 Json 响应。

There 是使用 Spray-Json 解析此 Google Map Json 响应的一个简洁示例:


   "results" : [
      
         "elevation" : 8815.7158203125,
         "location" : 
            "lat" : 27.988056,
            "lng" : 86.92527800000001
         ,
         "resolution" : 152.7032318115234
      
   ],
   "status" : "OK"

在上面的例子中,最外层是Object。但是,我需要直接解析一个Json响应,其最外层是由容器信息组成的Array,如下所示:

[
     
       "Id": "8dfafdbc3a40",
       "Image": "base:latest",
       "Command": "echo 1",
       "Created": 1367854155,
       "Status": "Exit 0",
       "Ports":["PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"],
       "SizeRw":12288,
       "SizeRootFs":0
     ,
      ... ,
      ... 
]

这是我改编自 Google 地图示例的代码:

package main

import ...

case class Container(id: String, image: String, command: String, created: Long, status: String, ports: List[Port], sizeRW: Long, sizeRootFs: Long)
case class Port(privatePort: Long, publicPort: Long, portType: String)
case class DockerApiResult[T](results: List[T])

object ContainerListJsonProtocol extends DefaultJsonProtocol 
  implicit val portFormat = jsonFormat3(Port)
  implicit val containerFormat = jsonFormat8(Container)
  implicit def dockerApiResultFormat[T :JsonFormat] = jsonFormat1(DockerApiResult.apply[T])


object Main extends App 

  implicit val system = ActorSystem("simple-spray-client")
  import system.dispatcher // execution context for futures below
  val log = Logging(system, getClass)

  log.info("Requesting containers info...")

  import ContainerListJsonProtocol._
  import SprayJsonSupport._
  val pipeline = sendReceive ~> unmarshal[DockerApiResult[Container]]

  val responseFuture = pipeline 
    Get("http://<ip-address>:4243/containers/json")
  

  responseFuture onComplete 
    case Success(DockerApiResult(Container(_,_,_,_,_,_,_,_) :: _)) =>
      log.info("Id of the found image:  ")
      shutdown()

    case Success(somethingUnexpected) =>
      log.warning("The Docker API call was successful but returned something unexpected: ''.", somethingUnexpected)
      shutdown()

    case Failure(error) =>
      log.error(error, "Couldn't get containers information")
      shutdown()
  

  def shutdown(): Unit = 
    IO(Http).ask(Http.CloseAll)(1.second).await
    system.shutdown()
  

以下是我得到的异常 (Object expected):

spray.httpx.PipelineException: MalformedContent(Object expected,Some(spray.json.DeserializationException: Object expected))

我当然错过了一些明显的东西,但是 如何使用 Spray-Json 解析 Json 数组?

另外,有没有一种简单的方法可以做到这一点,而不必处理自定义 JsonFormat 或 RootJsonFormat?

【问题讨论】:

就个人而言,我会切换到 Lift-json :) 【参考方案1】:

通过unmarshal[DockerApiResult[Container]],您是在告诉 spray-json 您希望格式是表单的 json 对象:

 results: [...] 

因为case class DockerApiResult[T](results: List[T]) 被定义为具有包含列表的单个结果字段的对象。

相反,您需要这样做:

unmarshal[List[Container]]

然后直接对生成的列表进行操作(或者通过spray-json解析后包装在DockerApiResult中)。

如果你想将 spray-json 直接解组为 DockerApiResult,你可以编写一个 JsonFormat,如下所示:

implicit object DockerApiResultFormat extends RootJsonFormat[DockerApiResult] 
  def read(value: JsValue) = DockerApiResult(value.convertTo[List[Container]])
  def write(obj: DockerApiResult) = obj.results.toJson

【讨论】:

谢谢!有效。我实际上尝试了unmarshal[List[Container]],但我对为空的端口属性感到困惑,我不得不用Option 包装它。无论如何,很好的答案,正是我想要的;)【参考方案2】:

对此进行了一些斗争,并找到了一种使用喷雾从 json 解析字符串转换为 JsArray 的方法:

import spray.json._   //parseJson
val kkkk =
  """
    |["a": "1", "b": "2"]
  """.stripMargin.parseJson.asInstanceOf[JsArray]

【讨论】:

这是否保持元素的顺序?

以上是关于Spray-Json:如何解析 Json 数组?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spray-json 解析简单数组

spray-json

当 json 元素不存在时,如何设置 spray-json 以设置 null?

如何使用 AKKA-HTTP、spray-json、oauth2 和 slick 优化 scala REST api?

spray-json JsString 对字符串值的引用

Spray-json反序列化嵌套对象