问题
The following Scala code compiles:
import spray.json.DefaultJsonProtocol._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives.complete
case class Item(name: String, id: Long)
implicit val itemFormat = jsonFormat2(Item)
val item = Item("xbox", 123)
complete(item)
with the following output in the worksheet:
import spray.json.DefaultJsonProtocol._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives.complete
defined class Item
itemFormat: spray.json.RootJsonFormat[Item] = spray.json.ProductFormatsInstances$$anon$2@4528e00
item: Item = Item(xbox,123)
res0: akka.http.scaladsl.server.StandardRoute = <function1>
But when I comment out the import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
I get the following compilation error:
Error:(11, 11) type mismatch;
found : A$A562.this.Item
required: akka.http.scaladsl.marshalling.ToResponseMarshallable
complete(item);}
^
How does that import have this affect?
回答1:
When you do RequestContext#complete(response)
, which takes ToResponseMarshallable
as input.
package akka.http.scaladsl.server
@InternalApi
private[http] class RequestContextImpl(
override def complete(trm: ToResponseMarshallable): Future[RouteResult] =
trm(request)(executionContext)
.fast.map(res ⇒ RouteResult.Complete(res))(executionContext)
.fast.recover {
case Marshal.UnacceptableResponseContentTypeException(supported) ⇒
RouteResult.Rejected(UnacceptedResponseContentTypeRejection(supported) :: Nil)
case RejectionError(rej) ⇒
RouteResult.Rejected(rej :: Nil)
}(executionContext)
}
SprayJsonSupport
is the object where implicit Marshallers
are defined and they are the ones which gives Marshallable
package akka.http.scaladsl.marshallers.sprayjson
trait SprayJsonSupport {
implicit def sprayJsonMarshallerConverter[T](writer: RootJsonWriter[T])(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] =
sprayJsonMarshaller[T](writer, printer)
implicit def sprayJsonMarshaller[T](implicit writer: RootJsonWriter[T], printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] =
sprayJsValueMarshaller compose writer.write
implicit def sprayJsValueMarshaller(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[JsValue] =
Marshaller.StringMarshaller.wrap(MediaTypes.`application/json`)(printer)
}
If you don't import SprayJsonSupport
, you won't get implicit Marshallers
which marshall your case class to desired output which is JSObject
.
If you don't want to import SprayJsonSupport
that provides default toJsonMarshallers, write your own, or copy paste the marshallers from JsonSpraySupport
.
example
object GetHttpRoutes {
case class Acknowledge(status: String)
implicit val itemFormat = jsonFormat1(Acknowledge)
implicit def toJsonMarshallerConverter[Entity](writer: RootJsonWriter[Entity])(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[Entity] =
toJsonMarshaller[Entity](writer, printer)
implicit def toJsonMarshaller[Entity](implicit writer: RootJsonWriter[Entity], printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[Entity] =
toJsValueMarshaller compose writer.write
implicit def toJsValueMarshaller(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[JsValue] =
Marshaller.StringMarshaller.wrap(MediaTypes.`application/json`)(printer)
val get_api =
path("") {
get { context =>
context.complete {
Acknowledge(status = "xbox")
}
}
}
}
trait HTTPRoutes {
implicit val system: ActorSystem
implicit val materializer: ActorMaterializer
val route = GetHttpRoutes.get_api
}
tests
class GetHttpRoutesCompSpecs extends WordSpec with Matchers with ScalatestRouteTest with BeforeAndAfterAll {
"HTTP GET endpoints" should {
"returns xbox on /" in {
Get("/") ~> GetHttpRoutes.get_api ~> check {
responseAs[String] shouldEqual """{"status":"xbox"}"""
}
}
}
}
来源:https://stackoverflow.com/questions/45068547/how-is-akka-http-marshaling-implemented-under-the-hood