Akka(34): Http:Unmarshalling,from Json

来源:互联网 时间:2017-10-31

  Unmarshalling是Akka-http内把网上可传输格式的数据转变成程序高级结构话数据的过程,比如把Json数据转换成某个自定义类型的实例。按具体流程来说就是先把Json转换成可传输格式数据如:MessageEntity,HttpRequest,HttpReponse等,然后再转换成程序高级结构数据如classXX实例。Unmarshalling对一个A类实例到B类实例的转换是通过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))

...}

从Unmarshaller的构建函数apply可以估计它的作用应该与函数A=>Future[B]很相似。A代表网上可传输类型如MessageEntity、HttpRequest,B代表某种程序高级数据类型。因为A到B的转换是non-blocking的,所以可以立即返回Future类型结果。Akka-http按被转换对象类型分类命名了下面这些类型别名:

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对以下类型提供了自动的Unmarshalling转换: 

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

也就是说Akka-http提供了这些U类型的Unmarshaller[U,B]隐式实例。Akka-http也提供了工具类型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)

}

}

我们可以通过Unmarshal.to[B]把Unmarshal[A]转换成Future[B]。注意:这一步只包括了从网上可传输类型到程序类型转换这一过程,不包括具体实现时的Json转换。下面是一些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")))

}

以上都是已知类型之间转换,可能没什么实际用途,不像marshalling:中间层Marshalling有实际转换的需要。Unmarshalling可以直接进行Json到自定义类型之间的转换,如:

 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"))

}}

以上是通过Directive as[???]实现的:

 /**

* Returns the in-scope [[FromRequestUnmarshaller]] for the given type.

*

* @group marshalling

*/

def as[T](implicit um: FromRequestUnmarshaller[T]) = um

这需要把FromRequestUmarshaller[T]放在可视域内,FromRequestUmarshaller[T]实际是Unmarshaller[T,B]的别名: 

type FromRequestUnmarshaller[T] = Unmarshaller[HttpRequest, T]

在上篇讨论我们介绍了Akka-http的Marshalling是type-class模式的。其中关键可以参考上篇讨论。现在我们需要这些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._

...

如果使用Json4s的实现方式,我们需要如下提供这些隐式实例:

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

Json4s的具体用例如下:

 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"))

}}

从功能上和表达灵活性来讲,Json4s的实现方式要占优。

下面就是本次讨论的示范源代码:

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"))

}}

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相关阅读:
Top