Instantly share code, notes, and snippets.

Embed
What would you like to do?
akka-http and JSON

JSON handling in akka-http.

Marshaller and Unmarshaller

3rd parties

akka-http-json

Custom JSON (un)marshaller for akka-http by using 3rd party libraries. The community seems to be very active.

akka-http + Jackson

akka-http + circe

circe's decode[A] produces Either[Error, A] but akka-http requires Future[A]. _.fold(Future.failed, Future.successful) can be a solution to convert from Either[Throwable, A] to Future[A].

Documents (in Japanese)

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpMethods, HttpRequest, StatusCodes, Uri}
import akka.http.scaladsl.unmarshalling.{FromEntityUnmarshaller, Unmarshal}
import akka.stream.ActorMaterializer
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
object UserService {
case class User(id: Long, name: String, firstName: String, lastName: String, location: Location)
case class Location(country: String, city: String)
}
trait UserServiceJsonSupport extends FailFastCirceSupport {
import UserService._
import io.circe.Decoder
// enable automatic decoder derivation
import io.circe.generic.auto._
/**
* ```
* {
* "user": {
* "id": 1,
* "name": "okapies",
* "first_name": "Yuta",
* "last_name": "Okamoto",
* "location": {
* "country": "Japan",
* "city": "Tokyo"
* }
* }
* }
* ```
*/
implicit val tokenInfoDecoder: Decoder[User] =
Decoder.forProduct5("id", "name", "first_name", "last_name", "location")(User).prepare(_.downField("user"))
// Note: The decoder for Location will be automatically derived by circe.
}
class UserServiceClient(val endpoint: Uri)
(implicit val system: ActorSystem,
val materializer: ActorMaterializer,
val executionContext: ExecutionContext) extends UserServiceJsonSupport {
import UserService._
def getUser(username: String): Future[User] =
doRequest[User](HttpRequest(uri = Uri.parseAndResolve(s"/api/v1/users/$username", base = endpoint)))
private[this] def doRequest[A: FromEntityUnmarshaller](request: HttpRequest): Future[A] =
Http().singleRequest(request).transformWith {
case Success(res) if res.status == StatusCodes.OK =>
Unmarshal(res.entity).to[A]
case Success(res) =>
res.discardEntityBytes()
Future.failed(new Exception(s"HttpRequest failed: $res"))
case Failure(t) =>
Future.failed(t)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment