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
def versions = [
  ScalaBinary: "2.13"
]
dependencies {
  implementation platform("com.typesafe.akka:akka-http-bom_${versions.ScalaBinary}:10.6.3")

  implementation "com.typesafe.akka:akka-http-spray-json_${versions.ScalaBinary}"
}
Maven
<properties>
  <scala.binary.version>2.13</scala.binary.version>
</properties>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-http-bom_${scala.binary.version}</artifactId>
      <version>10.6.3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <dependency>
    <groupId>com.typesafe.akka</groupId>
    <artifactId>akka-http-spray-json_${scala.binary.version}</artifactId>
  </dependency>
</dependencies>

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
sourceimport 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
sourceUnmarshaller<ByteString, JavaTweet> unmarshal = Jackson.byteStringUnmarshaller(JavaTweet.class);
JsonEntityStreamingSupport support = EntityStreamingSupport.json();

// imagine receiving such response from a service:
String payload = "{\"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.\"}";
HttpEntity.Strict entity = HttpEntities.create(ContentTypes.APPLICATION_JSON, payload);
HttpResponse response = HttpResponse.create().withEntity(entity);

Source<JavaTweet, Object> tweets =
    response.entity().getDataBytes()
    .via(support.framingDecoder()) // apply JSON framing
    .mapAsync(1, // unmarshal each element
        bs -> unmarshal.unmarshal(bs, system())
    );

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
sourceimport 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.

Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.