问题
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