2013-12-11 108 views
6

我是Spray-Json API的新手,我試圖解析來自Docker REST API的Json響應。Spray-Json:如何解析Json數組?

There是噴霧的Json的使用的一個例子清潔解析此谷歌地圖JSON響應:

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

在上述例子中的最外層是Object。然而,我需要直接解析其最外層是容器信息構成的Array JSON響應如下所示:

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

這裏是我改編自谷歌地圖示例的代碼:

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?

+0

就我個人而言,我會切換到Lift-json :) –

回答

13

這樣做unmarshal[DockerApiResult[Container]],你告訴噴霧JSON您所期望的格式的形式爲JSON對象:

{ results: [...] } 

因爲case class DockerApiResult[T](results: List[T])被定義爲包含一個單一的結果場中的目標名單。

相反,你需要做的:

unmarshal[List[Container]] 

,然後在結果列表上直接操作(或包裝在一個DockerApiResult已經通過噴霧JSON解析後)。

如果你想噴霧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 
} 
+0

謝謝!有效。我實際上嘗試過使用'unmarshal [List [Container]]',但我對Port屬性感到困惑,它是null,我不得不用'Option'來包裝它。無論如何,偉大的答案,正是我期待的;) – abronan

6

與此有點戰鬥,找到了一種方法,從解析的JSON轉換爲JsArray字符串使用噴霧:

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