2013-12-11 18 views
6

Jestem nowy w API Spray-Json i próbuję przeanalizować odpowiedź Json z interfejsu API REST Docker.Spray-Json: Jak przeanalizować układ Json?

There jest czystym przykładem wykorzystania Spray-JSON analizować tę odpowiedź Google Map JSON:

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

w powyższym przykładzie poziom oddalonych stanowi Object. Jednak muszę bezpośrednio analizować odpowiedzi JSON, którego najbardziej zewnętrzna poziom jest Array składa się z informacji kontenerów, jak pokazano poniżej:

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

Oto kod, który przystosowany na przykładzie map 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() 
    } 
} 

A poniżej jest wyjątkiem uzyskać (Object expected):

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

pewno brakuje czegoś oczywiste, ale Jak analizować Json Array za pomocą Spray-Json?

Czy istnieje prosty sposób na zrobienie tego bez konieczności radzenia sobie z niestandardowymi JsonFormat lub RootJsonFormat?

+0

Osobiście przełączyłem się na Lift-json :) –

Odpowiedz

13

Robiąc unmarshal[DockerApiResult[Container]], mówisz sprayu-json, że można oczekiwać, aby być w formacie JSON obiekt w postaci:

{ results: [...] } 

od case class DockerApiResult[T](results: List[T]) jest zdefiniowany jako obiekt z wynikami jednego pola zawierającego lista.

Zamiast tego trzeba zrobić:

unmarshal[List[Container]] 

a następnie działać na liście wynikowej bezpośrednio (lub owinąć go w DockerApiResult po to został przeanalizowany przez rozpylanie json).

Jeśli chcesz sprayu-json do unmarshal bezpośrednio do DockerApiResult, można napisać JsonFormat z czymś takim:

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

Dzięki! Zadziałało. Naprawdę próbowałem z 'unmarshal [List [Container]]' ale myliłem się z atrybutem Port, który był zerowy i musiałem zawinąć go 'Opcją'. W każdym razie świetna odpowiedź, dokładnie to, czego szukałem;) – abronan

6

walczyła z tym trochę i znaleźć sposób na konwersję do JsArray z json analizowany string za pomocą sprayu:

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