JSON Support

JSON Support

Akka HTTP's marshalling and unmarshalling infrastructure makes it rather easy to seamlessly support specific wire representations of your data objects, like JSON, XML or even binary encodings.

For JSON Akka HTTP currently provides support for spray-json right out of the box through it's akka-http-spray-json module.

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.

This is how you enable automatic support for (un)marshalling from and to JSON with spray-json:

  1. Add a library dependency onto "com.typesafe.akka" %% "akka-http-spray-json-experimental" % "2.4.4".
  2. import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ or mix in the akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport trait.
  3. 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.

Once you have done this (un)marshalling between JSON and your type T should work nicely and transparently.

    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 = jsonFormat2(Item)
      implicit val orderFormat = jsonFormat1(Order) // contains List[Item]
    }

    // use it wherever json (un)marshalling is needed
    class MyJsonService extends Directives with JsonSupport {

      // format: OFF
      val route =
        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")
          }
        }
      // format: ON
    }
  }

  "second-spray-json-example" in compileOnlySpec {
    import akka.actor.ActorSystem
    import akka.stream.ActorMaterializer
    import akka.Done
    import akka.http.scaladsl.server.Route
    import akka.http.scaladsl.server.Directives._
    import akka.http.scaladsl.model.StatusCodes
    import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
    import spray.json.DefaultJsonProtocol._

    object WebServer {

      // domain model
      final case class Item(name: String, id: Long)
      final case class Order(items: List[Item])

      // formats for unmarshalling and marshalling
      implicit val itemFormat = jsonFormat2(Item)
      implicit val orderFormat = jsonFormat1(Order)

      // (fake) async database query api
      def fetchItem(itemId: Long): Future[Option[Item]] = ???
      def saveOrder(order: Order): Future[Done] = ???

      def main(args: Array[String]) {

        // needed to run the route
        implicit val system = ActorSystem()
        implicit val materializer = ActorMaterializer()

        val route: Route =
          get {
            pathPrefix("item" / LongNumber) { id =>
              // there might be no item for a given id
              val maybeItem: Future[Option[Item]] = fetchItem(id)

              onSuccess(maybeItem) {
                case Some(item) => complete(item)
                case None       => complete(StatusCodes.NotFound)
              }
            }
          } ~
            post {
              path("create-order") {
                entity(as[Order]) { order =>
                  val saved: Future[Done] = saveOrder(order)
                  onComplete(saved) { done =>
                    complete("order created")
                  }
                }
              }
            }

      }
    }
  }
}

To learn more about how spray-json works please refer to its documentation.

Contents