JSON Support
Akka HTTP’s marshalling and unmarshalling infrastructure makes it rather easy to seamlessly convert application-domain objects from and to JSON. Integration with spray-json is provided out of the box through the akka-http-spray-json
module. Integration with other JSON libraries are supported by the community. See the list of current community extensions for Akka HTTP.
spray-json Support
The SprayJsonSupport
trait provides a FromEntityUnmarshaller[T]
and ToEntityMarshaller[T]
for every type T
that an implicit spray.json.RootJsonReader
and/or spray.json.RootJsonWriter
(respectively) is available for.
To enable automatic support for (un)marshalling from and to JSON with spray-json, add a library dependency onto:
- sbt
val AkkaHttpVersion = "10.6.3" libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion
- Gradle
- Maven
Next, provide a RootJsonFormat[T]
for your type and bring it into scope. Check out the spray-json documentation for more info on how to do this.
Finally, import the FromEntityUnmarshaller[T]
and ToEntityMarshaller[T]
implicits directly from SprayJsonSupport
as shown in the example below or mix the akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
trait into your JSON support module.
Once you have done this (un)marshalling between JSON and your type T
should work nicely and transparently.
sourceimport akka.http.scaladsl.server.Directives
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json._
// domain model
final case class Item(name: String, id: Long)
final case class Order(items: List[Item])
// collect your json format instances into a support trait:
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
implicit val itemFormat: RootJsonFormat[Item] = jsonFormat2(Item.apply)
implicit val orderFormat: RootJsonFormat[Order] = jsonFormat1(Order.apply) // contains List[Item]
}
// use it wherever json (un)marshalling is needed
class MyJsonService extends Directives with JsonSupport {
val route =
concat(
get {
pathSingleSlash {
complete(Item("thing", 42)) // will render as JSON
}
},
post {
entity(as[Order]) { order => // will unmarshal JSON to Order
val itemsCount = order.items.size
val itemNames = order.items.map(_.name).mkString(", ")
complete(s"Ordered $itemsCount items: $itemNames")
}
}
)
}
Consuming JSON Streaming style APIs
A popular way of implementing streaming APIs is JSON Streaming (see Source Streaming for documentation on building server-side of such API).
Depending on the way the API returns the streamed JSON (newline delimited, raw sequence of objects, or “infinite array”) you may have to apply a different framing mechanism, but the general idea remains the same: consuming the infinite entity stream and applying a framing to it, such that the single objects can be easily deserialized using the usual marshalling infrastructure:
- Scala
-
source
import MyJsonProtocol._ import akka.http.scaladsl.unmarshalling._ import akka.http.scaladsl.common.EntityStreamingSupport import akka.http.scaladsl.common.JsonEntityStreamingSupport implicit val jsonStreamingSupport: JsonEntityStreamingSupport = EntityStreamingSupport.json() val input = """{"uid":1,"txt":"#Akka rocks!"}""" + "\n" + """{"uid":2,"txt":"Streaming is so hot right now!"}""" + "\n" + """{"uid":3,"txt":"You cannot enter the same river twice."}""" val response = HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, input)) // unmarshal: val unmarshalled: Future[Source[Tweet, NotUsed]] = Unmarshal(response).to[Source[Tweet, NotUsed]] // flatten the Future[Source[]] into a Source[]: val source: Source[Tweet, Future[NotUsed]] = Source.futureSource(unmarshalled)
- Java
In the above example the marshalling is handled by the implicitly provided JsonEntityStreamingSupport
, which is also used when building server-side streaming APIs. You can also achieve the same more explicitly, by manually connecting the entity byte stream through a framing and then deserialization stage:
- Scala
-
source
import MyJsonProtocol._ import akka.http.scaladsl.unmarshalling._ import akka.http.scaladsl.common.EntityStreamingSupport import akka.http.scaladsl.common.JsonEntityStreamingSupport implicit val jsonStreamingSupport: JsonEntityStreamingSupport = EntityStreamingSupport.json() val input = """{"uid":1,"txt":"#Akka rocks!"}""" + "\n" + """{"uid":2,"txt":"Streaming is so hot right now!"}""" + "\n" + """{"uid":3,"txt":"You cannot enter the same river twice."}""" val response = HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, input)) val value: Source[Tweet, Any] = response.entity.dataBytes .via(jsonStreamingSupport.framingDecoder) // pick your Framing (could be "\n" etc) .mapAsync(1)(bytes => Unmarshal(bytes).to[Tweet]) // unmarshal one by one
Pretty printing
By default, spray-json marshals your types to compact printed JSON by implicit conversion using CompactPrinter
, as defined in:
sourceimplicit def sprayJsonMarshallerConverter[T](writer: RootJsonWriter[T])(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] =
sprayJsonMarshaller[T](writer, printer)
Alternatively to marshal your types to pretty printed JSON, bring a PrettyPrinter
in scope to perform implicit conversion.
sourceimport akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import spray.json._
// domain model
final case class PrettyPrintedItem(name: String, id: Long)
object PrettyJsonFormatSupport {
import DefaultJsonProtocol._
implicit val printer: JsonPrinter = PrettyPrinter
implicit val prettyPrintedItemFormat: RootJsonFormat[PrettyPrintedItem] = jsonFormat2(PrettyPrintedItem.apply)
}
// use it wherever json (un)marshalling is needed
class MyJsonService extends Directives {
import PrettyJsonFormatSupport._
// format: OFF
val route =
get {
pathSingleSlash {
complete {
PrettyPrintedItem("akka", 42) // will render as JSON
}
}
}
// format: ON
}
val service = new MyJsonService
// verify the pretty printed JSON
Get("/") ~> service.route ~> check {
responseAs[String] shouldEqual
"""{""" + "\n" +
""" "id": 42,""" + "\n" +
""" "name": "akka"""" + "\n" +
"""}"""
}
To learn more about how spray-json works please refer to its documentation.