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-jsonJackson is provided out of the box through the akka-http-spray-json
akka-http-jackson
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
libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % "10.0.15"
- Gradle
dependencies { compile group: 'com.typesafe.akka', name: 'akka-http-spray-json_2.12', version: '10.0.15' }
- Maven
<dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-http-spray-json_2.12</artifactId> <version>10.0.15</version> </dependency>
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.
import 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 = 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
}
Pretty printing
By default, spray-json marshals your types to compact printed JSON by implicit conversion using CompactPrinter
, as defined in:
implicit 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.
import 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 = PrettyPrinter
implicit val prettyPrintedItemFormat = jsonFormat2(PrettyPrintedItem)
}
// 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" +
""" "name": "akka",""" + "\n" +
""" "id": 42""" + "\n" +
"""}"""
}
To learn more about how spray-json works please refer to its documentation.
Jackson Support
To make use of the support module for (un)marshalling from and to JSON with Jackson, add a library dependency onto:
- sbt
libraryDependencies += "com.typesafe.akka" %% "akka-http-jackson" % "10.0.15"
- Gradle
dependencies { compile group: 'com.typesafe.akka', name: 'akka-http-jackson_2.12', version: '10.0.15' }
- Maven
<dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-http-jackson_2.12</artifactId> <version>10.0.15</version> </dependency>
Use akka.http.javadsl.marshallers.jackson.Jackson.unmarshaller(T.class)
to create an Unmarshaller<HttpEntity,T>Unmarshaller[HttpEntity,T] which expects the request body (HttpEntity) to be of type application/json
and converts it to T
using Jackson.
import akka.http.javadsl.marshallers.jackson.Jackson;
import akka.http.javadsl.model.StatusCodes;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static akka.http.javadsl.server.Directives.*;
import static akka.http.javadsl.unmarshalling.StringUnmarshallers.INTEGER;
public static Route appRoute(final Map<Integer, Pet> pets) {
PetStoreController controller = new PetStoreController(pets);
// Defined as Function in order to refer to [pets], but this could also be an ordinary method.
Function<Integer, Route> existingPet = petId -> {
Pet pet = pets.get(petId);
return (pet == null) ? reject() : complete(StatusCodes.OK, pet, Jackson.<Pet>marshaller());
};
// The directives here are statically imported, but you can also inherit from AllDirectives.
return
route(
path("", () ->
getFromResource("web/index.html")
),
pathPrefix("pet", () ->
path(INTEGER, petId -> route(
// demonstrates different ways of handling requests:
// 1. using a Function
get(() -> existingPet.apply(petId)),
// 2. using a method
put(() ->
entity(Jackson.unmarshaller(Pet.class), thePet ->
putPetHandler(pets, thePet)
)
),
// 2.1. using a method, and internally handling a Future value
path("alternate", () ->
put(() ->
entity(Jackson.unmarshaller(Pet.class), thePet ->
putPetHandler(pets, thePet)
)
)
),
// 3. calling a method of a controller instance
delete(() -> controller.deletePet(petId))
))
)
);
}
Use akka.http.javadsl.marshallers.jackson.Jackson.marshaller(T.class)
to create a Marshaller<T,RequestEntity>Marshaller[T,RequestEntity] which can be used with RequestContext.complete
or RouteDirectives.complete
to convert a POJO to an HttpResponse.
import akka.http.javadsl.marshallers.jackson.Jackson;
import akka.http.javadsl.model.StatusCodes;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static akka.http.javadsl.server.Directives.*;
import static akka.http.javadsl.unmarshalling.StringUnmarshallers.INTEGER;
private static Route putPetHandler(Map<Integer, Pet> pets, Pet thePet) {
pets.put(thePet.getId(), thePet);
return complete(StatusCodes.OK, thePet, Jackson.<Pet>marshaller());
}
private static Route alternativeFuturePutPetHandler(Map<Integer, Pet> pets, Pet thePet) {
pets.put(thePet.getId(), thePet);
CompletableFuture<Pet> futurePet = CompletableFuture.supplyAsync(() -> thePet);
return completeOKWithFuture(futurePet, Jackson.<Pet>marshaller());
}
Refer to this file in the sources for the complete example.