Reputation: 66609
Given a scala http client being parsed through json4s, I had the following working code to transform a Future of a HttpResponse into a Future of a case class:
def getJobIds(): Future[Seq[Job]] = {
...
var req = HttpRequest(
method = HttpMethods.GET,
uri = uri
)
val res: Future[HttpResponse] = Http().singleRequest(req)
res.flatMap {
case response@HttpResponse(StatusCodes.OK, _, _, _) => {
val buffer = ByteString("")
response.entity.dataBytes
.runFold(buffer)(_ ++ _)
.map(_.utf8String)
.map(
(body: String) => {
parse(body).as[Seq[Job]]
}
)
}
case _: Any => throw new Throwable("Error!")
}
}
Now I wanted to extract the logic to transform the HttpResponse to a case classes using generics in order to not repeat myself for other calls:
def getJobIds(): Future[Seq[Job]] = {
...
val res: Future[HttpResponse] = Http().singleRequest(req)
transformResponse[Seq[Job]](res)
}
private def transformResponse[T](r: Future[HttpResponse]): Future[T] = {
r.flatMap {
case response@HttpResponse(StatusCodes.OK, _, _, _) => {
val buffer = ByteString("")
response.entity.dataBytes
.runFold(buffer)(_ ++ _)
.map(_.utf8String)
.map(parse(_).extract[T])
}
case _: Any => throw new Throwable("Error!")
}
}
Yet this now throws:
Metronome.scala:46:32: No Manifest available for T
for the line
.map(parse(_).extract[T])
How do I make the code work with the use of generics?
Upvotes: 1
Views: 1067
Reputation: 66609
The issues comes from json4s extract
method.
If you look it up you'll find that its defintion is:
def extract[A](implicit formats: Formats, mf: scala.reflect.Manifest[A]): A =
Extraction.extract(jv)(formats, mf)
For some reason, the implicit Manifest look-up works for the concrete type, yet for the generic case you'll have to define the implicit manifest for your T
type:
private def transformResponse[T](r: Future[HttpResponse])
(implicit ev: scala.reflect.Manifest[T]): Future[T] = {
r.flatMap {
case response@HttpResponse(StatusCodes.OK, _, _, _) => {
val buffer = ByteString("")
response.entity.dataBytes
.runFold(buffer)(_ ++ _)
.map(_.utf8String)
.map(parse(_).extract[T])
}
case _: Any => throw new Throwable("Error!")
}
Now your code will compile and run.
Upvotes: 4