Unmarshalling is a process in Akka-http to convert data in a transportable format on the Internet into program high-level structured speech data, such as converting Json data into an instance of a custom type. According to the specific process, Json is first converted into transferable format data such as Message Entity, HttpRequest, HttpReponse, etc., and then into program high-level structure data such as classXX instance. Unmarshalling converts a class A instance to a class B instance through Unmarshaller[A,B]:
trait Unmarshaller[-A, B] extends akka.http.javadsl.unmarshalling.Unmarshaller[A, B] {...}
object Unmarshaller
extends GenericUnmarshallers
with PredefinedFromEntityUnmarshallers
with PredefinedFromStringUnmarshallers {
// format: OFF
//#unmarshaller-creation
/**
* Creates an `Unmarshaller` from the given function.
*/
def apply[A, B](f: ExecutionContext ⇒ A ⇒ Future[B]): Unmarshaller[A, B] =
withMaterializer(ec => _ => f(ec))
...}
From Unmarshaller's construction function apply, it can be estimated that its function should be very similar to function A=> Future [B]. A stands for transportable types such as Message Entity, HttpRequest, and B for advanced data types of a program. Because the conversion from A to B is non-blocking, you can return Future type results immediately. Akka-http named the following types of aliases according to the type of converted object:
type FromEntityUnmarshaller[T] = Unmarshaller[HttpEntity, T]
type FromMessageUnmarshaller[T] = Unmarshaller[HttpMessage, T]
type FromResponseUnmarshaller[T] = Unmarshaller[HttpResponse, T]
type FromRequestUnmarshaller[T] = Unmarshaller[HttpRequest, T]
type FromByteStringUnmarshaller[T] = Unmarshaller[ByteString, T]
type FromStringUnmarshaller[T] = Unmarshaller[String, T]
type FromStrictFormFieldUnmarshaller[T] = Unmarshaller[StrictForm.Field, T]
Akka-http provides automatic Unmarshalling transformations for the following types:
PredefinedFromStringUnmarshallers
Byte
Short
Int
Long
Float
Double
Boolean
PredefinedFromEntityUnmarshallers
Array[Byte]
ByteString
Array[Char]
String
akka.http.scaladsl.model.FormData
GenericUnmarshallers
Unmarshaller[T, T] (identity unmarshaller)
Unmarshaller[Option[A], B], if an Unmarshaller[A, B] is available
Unmarshaller[A, Option[B]], if an Unmarshaller[A, B] is available
That is to say, Akka-http provides implicit instances of these U-type Unmarshaller[U,B]. Akka-http also provides the tool type Unmarshal:
object Unmarshal {
def apply[T](value: T): Unmarshal[T] = new Unmarshal(value)
}
class Unmarshal[A](val value: A) {
/**
* Unmarshals the value to the given Type using the in-scope Unmarshaller.
*
* Uses the default materializer [[ExecutionContext]] if no implicit execution context is provided.
* If you expect the marshalling to be heavy, it is suggested to provide a specialized context for those operations.
*/
def to[B](implicit um: Unmarshaller[A, B], ec: ExecutionContext = null, mat: Materializer): Future[B] = {
val context: ExecutionContext = if (ec == null) mat.executionContext else ec
um(value)(context, mat)
}
}
We can convert Unmarshal[A] into Future[B] through Unmarshal.to[B]. Note: This step only covers the process of converting from a transferable type to a program type on the Internet, excluding the Json conversion at the time of implementation. Here are some use cases of Unmarshal:
import akka.actor._
import akka.stream._
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
object Unmarshalling {
implicit val httpSys = ActorSystem("httpSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEC = httpSys.dispatcher
val futInt = Unmarshal(43).to[Int]
val futBoolean = Unmarshal("0").to[Boolean]
val futString = Unmarshal(HttpEntity("Hello")).to[String]
val futHello = Unmarshal(HttpRequest(method = HttpMethods.GET, entity = HttpEntity("hello")))
}
All of these are known type-to-type conversions, which may be of little practical use, unlike marshalling: the mid-tier Marshalling has the need for practical conversions. Unmarshalling can directly convert Json to custom types, such as:
val route = (path("User") & post) { entity(as[User]){ user =>
complete(Future(s"inserting user: $user"))
}} ~
(path("Item"/IntNumber) & put) { id => entity(as[Item]){ item =>
complete(Future(s"update item $id: $item"))
}}
The above is achieved through Directive as [???]:
/**
* Returns the in-scope [[FromRequestUnmarshaller]] for the given type.
*
* @group marshalling
*/
def as[T](implicit um: FromRequestUnmarshaller[T]) = um
This requires that FromRequest Umarshaller [T] be placed in the visual domain, and FromRequest Umarshaller [T] is actually an alias for Unmarshaller[T,B]:
type FromRequestUnmarshaller[T] = Unmarshaller[HttpRequest, T]
In the previous discussion, we introduced that Marshalling of Akka-http is a type-class model. The key points can be referred to the previous discussion. Now we need these implicit examples of Unmarshaller:
trait Formats extends SprayJsonSupport with DefaultJsonProtocol object Converters extends Formats { case class User(id: Int, name: String) case class Item(id: Int, name: String, price: Double) implicit val itemFormat = jsonFormat3(Item.apply) implicit val userFormat = jsonFormat2(User.apply) } object Unmarshalling { import Converters._ ...
If we use the implementation of Json4s, we need to provide these implicit examples as follows:
trait JsonCodec extends Json4sSupport {
import org.json4s.DefaultFormats
import org.json4s.ext.JodaTimeSerializers
implicit val serilizer = jackson.Serialization
implicit val formats = DefaultFormats ++ JodaTimeSerializers.all
}
object JsConverters extends JsonCodec
The specific uses of Json4s are as follows:
import scala.collection.mutable._
case class User(id: Int, name: String)
class Item(id: Int, name: String, price: Double)
object AnyPic {
val area = 10
val title = "a picture"
val data = ArrayBuffer[Byte](1,2,3)
}
val route = (path("User") & post) { entity(as[User]){ user =>
complete(Future(s"inserting user: $user"))
}} ~
(path("Item"/IntNumber) & put) { id => entity(as[Item]){ item =>
complete(Future(s"update item $id: $item"))
}} ~
(path("Picture") & put) { entity(as[AnyPic.type]){ pic =>
complete(Future(s"insert picture: $pic"))
}}
In terms of function and expressive flexibility, Json4s has the advantage of implementation.
The following is the demo source code for this discussion:
Unmarshalling
import akka.actor._
import akka.stream._
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import scala.concurrent._
import akka.http.scaladsl.marshallers.sprayjson._
import spray.json._
trait Formats extends SprayJsonSupport with DefaultJsonProtocol
object Converters extends Formats {
case class User(id: Int, name: String)
case class Item(id: Int, name: String, price: Double)
implicit val itemFormat = jsonFormat3(Item.apply)
implicit val userFormat = jsonFormat2(User.apply)
}
object Unmarshalling {
import Converters._
implicit val httpSys = ActorSystem("httpSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEC = httpSys.dispatcher
val futInt = Unmarshal(43).to[Int]
val futBoolean = Unmarshal("0").to[Boolean]
val futString = Unmarshal(HttpEntity("Hello")).to[String]
val futHello = Unmarshal(HttpRequest(method = HttpMethods.GET, entity = HttpEntity("hello")))
val route = (path("User") & post) { entity(as[User]){ user =>
complete(Future(s"inserting user: $user"))
}} ~
(path("Item"/IntNumber) & put) { id => entity(as[Item]){ item =>
complete(Future(s"update item $id: $item"))
}}
}
Json4sUnmarshalling
import akka.actor._
import akka.stream._
import akka.http.scaladsl.server.Directives._
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s.jackson
import scala.concurrent._
trait JsonCodec extends Json4sSupport {
import org.json4s.DefaultFormats
import org.json4s.ext.JodaTimeSerializers
implicit val serilizer = jackson.Serialization
implicit val formats = DefaultFormats ++ JodaTimeSerializers.all
}
object JsConverters extends JsonCodec
object Json4sUnmarshalling {
import JsConverters._
implicit val httpSys = ActorSystem("httpSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEC = httpSys.dispatcher
import scala.collection.mutable._
case class User(id: Int, name: String)
class Item(id: Int, name: String, price: Double)
object AnyPic {
val area = 10
val title = "a picture"
val data = ArrayBuffer[Byte](1,2,3)
}
val route = (path("User") & post) { entity(as[User]){ user =>
complete(Future(s"inserting user: $user"))
}} ~
(path("Item"/IntNumber) & put) { id => entity(as[Item]){ item =>
complete(Future(s"update item $id: $item"))
}} ~
(path("Picture") & put) { entity(as[AnyPic.type]){ pic =>
complete(Future(s"insert picture: $pic"))
}}
}