Spray : How to Unmarshal a response of JsArray or JsObject (ie JsValue) in pipeline

919 views Asked by At

I am creating a service that aggregates data and will need to be able to read any unknown JSON document. I have the pipeline defined as follows:

  private def pipeline = (
       addHeader("Accept", "application/json")
    ~> sendReceive
    ~> unmarshal[JsObject] // Need this to work for JsObject or JsArray //
    ~> recover
  )

This will work with a JsObject but not a JsArray. If I change it to a JsArray then it will not (of course) work with a JsObject. My recover method returns a JsObject.

I would love to be able to define this as a JsValue or enforce a Root format, but for JsValue I get the following compiler error:

could not find implicit value for evidence parameter of type spray.httpx.unmarshalling.FromResponseUnmarshaller[spray.json.JsValue]

And Root Formats also error.

I am not sure how to accomplish what I need, any help would be appreciated.

2

There are 2 answers

2
Mustafa Simav On BEST ANSWER

Use Either, Eric! :) If the response will be either JsObject or JsArray then Either is good solution.

private def pipeline =
       addHeader("Accept", "application/json")
    ~> sendReceive
    ~> unmarshal[Either[JsObject, JsArray]]
    ~> recover

However, beware that unmarshal[Either[JsObject, JsArray]] tries to parse response as JsObject first and if it fails, tries to parse it as JsArray. This may lead some performance issues.

1
Eric On

After reviewing @Mustafa's answer I created the following to avoid the potential performance hit. In the end, I really only need a JSON AST to pass on.

In the most simple terms, I simply created a function to handle it:

  def unmarshalJSON(httpResponse: HttpResponse): JsValue = {
    httpResponse.entity.asString.parseJson
  }

and altered below:

private def pipeline = {
     addHeader("Accept", "application/json")
  ~> sendReceive
  ~> unmarshalJSON
  ~> recover
}

I would of course want to beef this up a bit for production level code, but this could be another alternative and allows me to return a JsValue. @Mustafa I would be interested to hear your thoughts.