Spray-Json: как разобрать массив Json?
Я новичок в Spray-Json API и пытаюсь разобрать ответ 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?
2 ответа
При выполнении unmarshal[DockerApiResult[Container]]
Вы говорите Spray-JSON, что вы ожидаете, что формат будет объект JSON формы:
{ results: [...] }
поскольку case class DockerApiResult[T](results: List[T])
определяется как объект с одним полем результатов, содержащим список.
Вместо этого вам нужно сделать:
unmarshal[List[Container]]
а затем оперируйте полученным списком напрямую (или оберните его в DockerApiResult после того, как он был проанализирован spray-json).
Если вы хотите, чтобы 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
}
Немного поборолся с этим и нашел способ конвертировать в JsArray из проанализированной строки json с помощью spray:
import spray.json._ //parseJson
val kkkk =
"""
|[{"a": "1"}, {"b": "2"}]
""".stripMargin.parseJson.asInstanceOf[JsArray]