2013-12-11 10 views
6

Ich bin neu in der Spray-Json API und ich versuche, eine JSON-Antwort von der Docker REST API zu analysieren.Spray-Json: Wie man ein Json-Array analysiert?

There ist ein sauberes Beispiel für die Verwendung von Spray-Json dieser Google Map Json Antwort zu analysieren: die äußerste Ebene ist ein Object

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

Im obigen Beispiel. Ich brauche aber direkt eine Json Reaktion, deren äußerste Ebene parsen ist ein Array von Behältern Informationen zusammengesetzt, wie unten gezeigt:

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

Hier ist ein Code, den ich von dem Google Karte Beispiel angepasst:

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() 
    } 
} 

Und unten ist die Ausnahme, die ich bekommen (Object expected):

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

ich vermisse sicherlich etwas offensichtlich, aber Wie kann ein Json Array mit Spray-Json analysiert werden?

Gibt es auch eine einfache Möglichkeit, dies zu tun, ohne mit benutzerdefinierten JsonFormat oder RootJsonFormat umgehen zu müssen?

+0

Persönlich würde ich zu Lift-json wechseln :) –

Antwort

13

von unmarshal[DockerApiResult[Container]] tun, Sie sagen Spray-json, dass Sie das Format erwarten ein JSON-Objekt des Formulars zu sein:

{ results: [...] } 

seit case class DockerApiResult[T](results: List[T]) als ein Objekt mit einem Ergebnisfeld definiert ist ein enthält, Liste.

Stattdessen müssen Sie tun:

unmarshal[List[Container]] 

und dann auf die resultierende Liste arbeiten direkt (oder in einem DockerApiResult wickeln, nachdem sie durch Sprüht json analysiert worden ist).

Wenn Sie Spray-json wollen direkt in eine DockerApiResult entpacken, können Sie eine JsonFormat mit so etwas wie schreiben:

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

Danke! Es funktionierte. Ich habe es tatsächlich mit 'unmarshal [List [Container]] versucht', aber ich war verwirrt mit dem Port-Attribut, das null war und ich musste es mit 'Option' umhüllen. Wie auch immer, tolle Antwort, genau das, was ich gesucht habe;) – abronan

6

mit diesem ein wenig gekämpft und hat einen Weg gefunden, um JsArray von einem json zu konvertieren analysiert Schnur unter Verwendung des Sprays:

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