I'm new to the Spray-Json API and I'm trying to parse a Json response from the Docker REST API.
There is a clean example of the usage of Spray-Json to parse this Google Map Json response :
{
"results" : [
{
"elevation" : 8815.7158203125,
"location" : {
"lat" : 27.988056,
"lng" : 86.92527800000001
},
"resolution" : 152.7032318115234
}
],
"status" : "OK"
}
In the above example the outermost level is an Object
. However, I need to directly parse a Json response whose outermost level is an Array
composed of containers information as shown below :
[
{
"Id": "8dfafdbc3a40",
"Image": "base:latest",
"Command": "echo 1",
"Created": 1367854155,
"Status": "Exit 0",
"Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
"SizeRw":12288,
"SizeRootFs":0
},
{ ... },
{ ... }
]
Here is a code that I adapted from the Google map example :
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()
}
}
And below is the exception I get (Object expected
) :
spray.httpx.PipelineException: MalformedContent(Object expected,Some(spray.json.DeserializationException: Object expected))
I certainly miss something obvious but How to parse a Json Array using Spray-Json?
Also, is there a simple way to do this without having to deal with custom JsonFormat or RootJsonFormat?
Use the JSON. parse() method to pase a JSON array, e.g. JSON. parse(arr) . The method parses a JSON string and returns its JavaScript value or object equivalent.
When using the JSON.parse() on a JSON derived from an array, the method will return a JavaScript array, instead of a JavaScript object.
The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string. An optional reviver function can be provided to perform a transformation on the resulting object before it is returned.
JSON is JavaScript Object Notation is used for data interchange, Array of strings is an ordered list of values with string type. So on a whole, the 'JSON array of strings' represents an ordered list of values, and It can store multiple values. This is useful to store string, Boolean, number, or an object.
By doing unmarshal[DockerApiResult[Container]]
, you're telling spray-json that you expect the format to be a json object of the form:
{ results: [...] }
since case class DockerApiResult[T](results: List[T])
is defined as an object with a single results field containing a list.
Instead you need to do:
unmarshal[List[Container]]
and then operate on the resulting list directly (or wrap it in a DockerApiResult after it has been parsed by spray-json).
If you want spray-json to unmarshal directly into a DockerApiResult, you can write a JsonFormat with something like:
implicit object DockerApiResultFormat extends RootJsonFormat[DockerApiResult] {
def read(value: JsValue) = DockerApiResult(value.convertTo[List[Container]])
def write(obj: DockerApiResult) = obj.results.toJson
}
fought with this a little and found a way to convert to JsArray from a json parsed string using spray:
import spray.json._ //parseJson
val kkkk =
"""
|[{"a": "1"}, {"b": "2"}]
""".stripMargin.parseJson.asInstanceOf[JsArray]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With