Spray-Json: как разобрать массив Json?

Я новичок в API Spray-Json и пытаюсь проанализировать ответ Json от Docker REST API.

Здесь — чистый пример использования 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))

Я, конечно, упустил что-то очевидное, но Как разобрать массив Json с помощью Spray-Json?

Кроме того, есть ли простой способ сделать это без необходимости иметь дело с пользовательским JsonFormat или RootJsonFormat?


person abronan    schedule 11.12.2013    source источник
comment
Лично я бы перешел на Lift-json :)   -  person Ben Schmidt    schedule 11.12.2013


Ответы (2)


Выполняя unmarshal[DockerApiResult[Container]], вы сообщаете spray-json, что ожидаете, что формат будет объектом json в форме:

{ results: [...] }

поскольку case class DockerApiResult[T](results: List[T]) определяется как объект с одним полем результатов, содержащим список.

Вместо этого вам нужно сделать:

unmarshal[List[Container]]

а затем работать с полученным списком напрямую (или обернуть его в DockerApiResult после его анализа с помощью spray-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
}
person kong    schedule 12.12.2013
comment
Спасибо! Это сработало. На самом деле я пытался использовать unmarshal[List[Container]], но меня запутал атрибут Port, который был нулевым, и мне пришлось обернуть его Option. В любом случае отличный ответ, именно то, что я искал;) - person abronan; 12.12.2013

немного поборолся с этим и нашел способ конвертировать в JsArray из разобранной строки json с помощью спрея:

import spray.json._   //parseJson
val kkkk =
  """
    |[{"a": "1"}, {"b": "2"}]
  """.stripMargin.parseJson.asInstanceOf[JsArray]
person Marco Aurelio    schedule 06.02.2015
comment
Сохраняет ли это порядок элементов? - person Manali Bhosale; 17.04.2018