2
votes


I'm quite new to scala, I will appreciate if you can help me with the following question.
i'm using akka-http to send http request I would like to declare a generic send request method which transforms the http response body using Unmarshall to the generic type.
How should i cast the Unmarshal response to T ???

the send request implementation:


    def sendRequest[T](requestMessage :HttpRequestMessage) :Future[T] =
    {

        val promise = Promise[HttpResponse]() 
        val request = requestMessage.request -> promise

        queue.offer(request).flatMap {
          case QueueOfferResult.Enqueued => promise.future.flatMap[AnyRef] { 
            response => { 
                response.entity.toStrict(FiniteDuration(3,"seconds")).flatMap {
                  strict =>  Unmarshal(strict.getData).to[T]
                }
              }
          }
        }

    }

1

1 Answers

3
votes

If you take a look at the code of Unmarshal object, you can see that the type parameter used in to has a certain constraint - an implicit unmarshaller:

class Unmarshal[A](val value: A) {
  /**
   * Unmarshals the value to the given Type using the in-scope Unmarshaller.
   */
  def to[B](implicit um: Unmarshaller[A, B], ec: ExecutionContext, mat: Materializer): Future[B] = um(value)
}

When unmarshalling a value of type A to a value of type B, there must be an implicit value of type Unmarshaller[A, B] available in scope. There are many predefined unmarshallers (e.g. here are some commonly used ones for converting from String to various types such as Int, Boolean etc.), but you can also define your own. This is known as the type class pattern.

So if you want your code to work for some type T, you must ensure that there is an implicit unmarshaller for that type available in scope. This means that your method sendRequest must have an implicit parameter of type Unmarshaller[Data, T], where Data should be replaced by whatever is the actual type of strict,getData (I can't tell from your code).

So something like this:

def sendRequest[T](requestMessage: HttpRequestMessage)(implicit m: Unmarshaller[Data, T]): Future[T] // replace Data with concrete type

This allows the compilation of to[T], but now you have the obligation to actually have this implicit value in scope at the point of invocation of to[T]. As said earlier, you have two options - either import predefined unmarshallers (such as PredefinedFromStringUnmarshallers shown earlier), or define your own. I can't tell what T can be so I can't advise you further than this, but the main point is that as long as you provide the unmarshaller for the concrete type behind T (via import or define it manually), it should work; otherwise akka will have no idea how to unmarshall to your T.