Akka(34): Http:Unmarshalling,from Json详解编程语言

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/12827.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论