Unmarshalling/解组

“Unmarshalling” is the process of converting some kind of a lower-level representation, often a “wire format”, into a higher-level (object) structure. Other popular names for it are “Deserialization” or “Unpickling”.

“解组”是将某种低级别的表示(通过是”线上格式“)转换到高级别(对象)结构的过程。其它流行的叫法有“反序列化”(Deserialization)或“解开”(Unpickling)。

In Akka HTTP “Unmarshalling” means the conversion of a lower-level source object, e.g. a MessageEntity (which forms the “entity body” of an HTTP request or response) or a full HttpRequestHttpRequest or HttpResponseHttpResponse, into an instance of type T.

Akka HTTP 里的“解组”意味着转换一个低级别的源对象,例如:MessageEntity(构成一个 HTTP 请求或响应的“实体正文”)或者 HttpRequestHttpRequest 或者 HttpResponseHttpResponse 到类型 T 的实例。

Basic Design

基础设计

Unmarshalling of instances of type A into instances of type B is performed by an Unmarshaller<A, B>Unmarshaller[A, B].

解码类型 A 实例到类型 B 实例由 Unmarshaller<A, B>Unmarshaller[A, B] 执行。

Akka HTTP also predefines a number of helpful aliases for the types of unmarshallers that you’ll likely work with most:

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]

At its core an Unmarshaller<A, B>Unmarshaller[A, B] is very similar to a function A => Future[B]Function<A, CompletionStage<B>> and as such quite a bit simpler than its marshalling counterpart. The process of unmarshalling does not have to support content negotiation which saves two additional layers of indirection that are required on the marshalling side.

Unmarshaller<A, B>Unmarshaller[A, B] 的核心非常类似 函数 A => Future[B]Function<A, CompletionStage<B>>, 因此比 编组 对应的要简单得多。解组过程不需要内容协商,这样可以节省编组方需要的两个间接层。

Using unmarshallers

使用解组器

For an example on how to use an unmarshaller on the server side, see for example the Dynamic Routing Example. For the client side, see Processing Responses

在服务器端怎样使用解组的示例请见 动态路由示例 。 客户端示例请见 处理响应

Predefined Unmarshallers

预定义解组器

Akka HTTP already predefines a number of unmarshallers for the most common types. Specifically these are:

Akka HTTP 对于大多数类型已经预定义了一些解组器。具体来说,这些是:

Additional unmarshallers are available in separate modules for specific content types, such as JSON and XML.

Implicit Resolution

隐式处理

The unmarshalling infrastructure of Akka HTTP relies on a type-class based approach, which means that UnmarshallerUnmarshaller instances from a certain type A to a certain type B have to be available implicitly.

Akka HTTP 解组的基础设施实现基于(Scala)类型类方法,这意味着从 A 类型到 B 类型的 UnmarshallerUnmarshaller 实例必需作为一个隐式参数/函数存在。

The implicits for most of the predefined unmarshallers in Akka HTTP are provided through the companion object of the UnmarshallerUnmarshaller trait. This means that they are always available and never need to be explicitly imported. Additionally, you can simply “override” them by bringing your own custom version into local scope.

Akka HTTP 中大部分预定义的解组器的隐式工具都是通过 UnmarshallerUnmarshaller trait 的伴身对象提供的。这意味着它们总是可用,而不需要显示导入。 另外,你可以在局部可视范围里用自己的版本覆盖原有版本。

Custom Unmarshallers

自定义解组器

Akka HTTP gives you a few convenience tools for constructing unmarshallers for your own types. Usually you won’t have to “manually” implement the UnmarshallerUnmarshaller traitclass directly. Rather, it should be possible to use one of the convenience construction helpers defined on the UnmarshallerUnmarshaller companionUnmarshallerUnmarshaller:

Akka HTTP 为构造你自己类型的解组器提供了一些便利工具。通常不需要直接“手动”实现 UnmarshallerUnmarshaller trait。 相反,应该使用在 UnmarshallerUnmarshaller 伴身对象UnmarshallerUnmarshaller 中定义的便利构造助手。

Scala
/**
 * Creates an `Unmarshaller` from the given function.
 */
def apply[A, B](f: ExecutionContext => A => Future[B]): Unmarshaller[A, B] =
  withMaterializer(ec => _ => f(ec))

def withMaterializer[A, B](f: ExecutionContext => Materializer => A => Future[B]): Unmarshaller[A, B] =
  new Unmarshaller[A, B] {
    def apply(a: A)(implicit ec: ExecutionContext, materializer: Materializer) =
      try f(ec)(materializer)(a)
      catch { case NonFatal(e) => FastFuture.failed(e) }
  }

/**
 * Helper for creating a synchronous `Unmarshaller` from the given function.
 */
def strict[A, B](f: A => B): Unmarshaller[A, B] = Unmarshaller(_ => a => FastFuture.successful(f(a)))

/**
 * Helper for creating a "super-unmarshaller" from a sequence of "sub-unmarshallers", which are tried
 * in the given order. The first successful unmarshalling of a "sub-unmarshallers" is the one produced by the
 * "super-unmarshaller".
 */
def firstOf[A, B](unmarshallers: Unmarshaller[A, B]*): Unmarshaller[A, B] = //...
Java
<A, B> Unmarshaller<A, B> async(java.util.function.Function<A, java.util.concurrent.CompletionStage<B>> f);
<A, B> Unmarshaller<A, B> sync(java.util.function.Function<A, B> f);
<A, B> Unmarshaller<A, B> firstOf(Unmarshaller<A, B> u1, Unmarshaller<A, B> u2);
<A, B> Unmarshaller<A, B> firstOf(Unmarshaller<A, B> u1, Unmarshaller<A, B> u2, Unmarshaller<A, B> u3);
<A, B> Unmarshaller<A, B> firstOf(Unmarshaller<A, B> u1, Unmarshaller<A, B> u2, Unmarshaller<A, B> u3, Unmarshaller<A, B> u4);
<A, B> Unmarshaller<A, B> firstOf(Unmarshaller<A, B> u1, Unmarshaller<A, B> u2, Unmarshaller<A, B> u3, Unmarshaller<A, B> u4, Unmarshaller<A, B> u5);
Note

To avoid unnecessary memory pressure, unmarshallers should make sure to either fully consume the incoming entity data stream, or make sure it is properly cancelled on error. Failure to do so might keep the remaining part of the stream in memory for longer than necessary.

为避免不必要的内存压力,解组器应确保完全消耗传入的实体数据流,或确保在出错时将其正确取消。否则,可能会将流的其余部分在内存中保留的时间比必要长。

Deriving Unmarshallers

衍生解组器

Sometimes you can save yourself some work by reusing existing unmarshallers for your custom ones. The idea is to “wrap” an existing unmarshaller with some logic to “re-target” it to your type.

有时,可通过复用已存在的解组器为自定义解组器节省一些工作。思路上就是以某种逻辑“包装”已存在的解组器以便“调整下目标”来对应你自己的类型。

Usually what you want to do is to transform the output of some existing unmarshaller and convert it to your type. For this type of unmarshaller transformation Akka HTTP defines these methods:

通常,想转换已存在的解组器上的输出,并将其转换到你的类型。对于此类解组器转换,Akka HTTP 定义了这些方法:

  • baseUnmarshaller.transform
  • baseUnmarshaller.map
  • baseUnmarshaller.mapWithInput
  • baseUnmarshaller.flatMap
  • baseUnmarshaller.flatMapWithInput
  • baseUnmarshaller.recover
  • baseUnmarshaller.withDefaultValue
  • baseUnmarshaller.mapWithCharset (仅适用于 FromEntityUnmarshallers)
  • baseUnmarshaller.forContentTypes (仅适用于 FromEntityUnmarshallers)

The method signatures should make their semantics relatively clear.

方法签名应使其语言相对清晰。

Using Unmarshallers

使用解组

In many places throughout Akka HTTP unmarshallers are used implicitly, e.g. when you want to access the entity of a request using the Routing DSL.

Akka HTTP 的很多地方,解组器被隐式使用,例如:当你想使用 路由 DSL 访问一个请求的 entity 时。

However, you can also use the unmarshalling infrastructure directly if you wish, which can be useful for example in tests. The best entry point for this is the akka.http.scaladsl.unmarshalling.Unmarshal objectakka.http.javadsl.unmarshalling.StringUnmarshallers class, which you can use like this:

但是,如果你希望,也可以直接使用编组器基础设施,例如在测试中可能很有用。@scala[akka.http.scaladsl.unmarshalling.Unmarshal object]akka.http.javadsl.unmarshalling.StringUnmarshallers 是最好的切入点,你可以这样使用:

Scala
import akka.http.scaladsl.unmarshalling.Unmarshal
import system.dispatcher // Optional ExecutionContext (default from Materializer)
implicit val materializer: Materializer = ActorMaterializer()

import scala.concurrent.Await
import scala.concurrent.duration._

val intFuture = Unmarshal("42").to[Int]
val int = Await.result(intFuture, 1.second) // don't block in non-test code!
int shouldEqual 42

val boolFuture = Unmarshal("off").to[Boolean]
val bool = Await.result(boolFuture, 1.second) // don't block in non-test code!
bool shouldBe false
Java
import akka.http.javadsl.model.*;
import akka.http.javadsl.unmarshalling.StringUnmarshallers;
import akka.http.javadsl.unmarshalling.Unmarshaller;

import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;

CompletionStage<Integer> integerStage =
  StringUnmarshallers.INTEGER.unmarshal("42", system().dispatcher(), materializer());
int integer = integerStage.toCompletableFuture().get(1, TimeUnit.SECONDS); // don't block in non-test code!
assertEquals(integer, 42);

CompletionStage<Boolean> boolStage =
  StringUnmarshallers.BOOLEAN.unmarshal("off", system().dispatcher(), materializer());
boolean bool = boolStage.toCompletableFuture().get(1, TimeUnit.SECONDS); // don't block in non-test code!
assertEquals(bool, false);
在此文档中发现错误?该页面的源代码可以在 这里 找到。欢迎随时编辑并提交 Pull Request。