0

I have defined a custom ToEntityMarshaller for type Organisation. When requesting localhost:8080/organisations it return an empty JSON array. Only when I remove the implicit def organisationMarshaller: ToEntityMarshaller[Organisation] it return the correct representation of the stream.

Anybody has an idea what is going on here?

import akka.NotUsed
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.common.{EntityStreamingSupport, JsonEntityStreamingSupport}
import akka.http.scaladsl.model.{HttpEntity, StatusCodes, _}
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller, ToResponseMarshaller}
import akka.http.scaladsl.model.TransferEncodings.gzip
import akka.http.scaladsl.model.headers.{HttpEncoding, HttpEncodings}
import akka.stream.scaladsl.{Flow, Source}
import akka.util.ByteString
import spray.json.DefaultJsonProtocol
import spray.json.DefaultJsonProtocol._

import scala.concurrent.Future
import scala.io.StdIn
import scala.util.Random

final case class Organisation(name: String, id: String)

trait Protocols extends DefaultJsonProtocol {

  import spray.json._

  implicit val organisationFormat = jsonFormat2(Organisation)

  val `vnd.example.api.v1+json` =
    MediaType.applicationWithFixedCharset("vnd.example.api.v1+json", HttpCharsets.`UTF-8`)

  // -- WORKS AFTER REMOVING THIS DECLARATION --
  implicit def organisationMarshaller: ToEntityMarshaller[Organisation] = Marshaller.oneOf(
    Marshaller.withFixedContentType(`vnd.example.api.v1+json`) { organisation =>
      HttpEntity(`vnd.example.api.v1+json`, organisation.toJson.compactPrint)
    })
}

object Server extends App with Protocols {
  implicit val system = ActorSystem("api")
  implicit val materializer = ActorMaterializer()
  implicit val executionContext = system.dispatcher

  implicit val jsonStreamingSupport: JsonEntityStreamingSupport = EntityStreamingSupport.json()
    .withParallelMarshalling(parallelism = 10, unordered = false)

  // (fake) async database query api
  def dummyOrganisation(id: String) = Organisation(s"Organisation $id", id.toString)

  def fetchOrganisation(id: String): Future[Option[Organisation]] = Future(Some(dummyOrganisation(id)))

  def fetchOrganisations(): Source[Organisation, NotUsed] = Source.fromIterator(() => Iterator.fill(10000) {
    val id = Random.nextInt()
    dummyOrganisation(id.toString)
  })

  val route =
    encodeResponse {
      pathPrefix("organisations") {
        get {
          val organisations = fetchOrganisations()
          complete(organisations)
        }
      }
    }


  val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

  println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
  StdIn.readLine()
  bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate())
}
Sebastian
  • 16,813
  • 4
  • 49
  • 56
  • Why did you create `organisationMarshaller` ? – expert Oct 21 '16 at 13:42
  • To control how entities are getting serialised based on custom media types – Sebastian Oct 21 '16 at 14:17
  • I see. Is it driven by media type requested by client ? What would be different based on requested media type ? E.g. they're all json but different format or it'll be json and non-json ? – expert Oct 21 '16 at 14:26
  • It is required for versioning the JSON formats. This would allow us to change the structure without breaking clients. – Sebastian Oct 21 '16 at 14:29

0 Answers0