Akka Http - 如何将 ResponseEntity 解组到 CustomClass?
Akka Http - How to Unmarshall ResponseEntity to CustomClass?
我正在使用 Akka Http 向第三方发出请求 API。响应是 "application/json",我想使用 Akka Http 将它们转换为自定义大小写 class。我想做这样的事情:
val request = RequestBuilding.Get("https://service.com/v1/api/items")
val response : Future[ItemsResponse] = http.singleRequest(request).flatMap({ response =>
Unmarshal(response.entity).to[ItemsResponse]
})
编译失败,因为我缺少类型为 akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.scaladsl.model.ResponseEntity, com.mycompany.models.ItemsResponse]
的隐式解组器。
我不清楚使用 akka http 执行此操作的惯用方法是什么。我知道我可以使用 spray-json,但我想了解如何在不导入另一个库的情况下执行此操作。 Akka Http 似乎是可行的,但文档不清楚(至少对我而言)。
最简单的方法是使用 spray-json,因为它是 Akka HTTP 的一部分:
import spray.json._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
// change 2 to the number of attributes of ItemsResponse
implicit val ItemsResponseFormat = jsonFormat2(ItemsResponse)
这应该可以编译您现有的代码。
我认为你的问题是有效的,在某些情况下避免额外的依赖是有意义的。我的是制作一个身份验证库,我不想将我的 JSON 库首选项强加给此类库的用户。库需要 JSON 解组以理解令牌信息响应。
给代码! :)
case class TokenInfo private (uid: String, realm: String, scope: Seq[String])
object TokenInfo {
private
def parseOpt(s: String): Option[TokenInfo] = {
util.parsing.json.JSON.parseFull(s) match {
case Some(map: Map[String,Any] @unchecked) =>
val tmp: Map[String,Any] = map.collect {
case (k@ "uid",x: String) => k -> x
case (k@ "realm",x: String) => k -> x
case (k@ "scope",x: Seq[String] @unchecked) => k -> x
// other keys are ignored
}.toMap
if (tmp.size == 3) {
Some( TokenInfo( tmp("uid").asInstanceOf[String], tmp("realm").asInstanceOf[String], tmp("scope").asInstanceOf[Seq[String]]) )
} else {
None
}
case _ => None
}
}
implicit
val unm: FromEntityUnmarshaller[TokenInfo] = {
PredefinedFromEntityUnmarshallers.stringUnmarshaller.map{ s => parseOpt(s).getOrElse{
throw new RuntimeException(s"Unknown TokenInfo: $s")
}}
}
}
我选择使用 Scala 自带的 util.parsing.json
。另一个选项只是正则表达式,但在那种情况下,我要么修复字段的预期顺序,要么代码可能会变得复杂。
我正在使用 Akka Http 向第三方发出请求 API。响应是 "application/json",我想使用 Akka Http 将它们转换为自定义大小写 class。我想做这样的事情:
val request = RequestBuilding.Get("https://service.com/v1/api/items")
val response : Future[ItemsResponse] = http.singleRequest(request).flatMap({ response =>
Unmarshal(response.entity).to[ItemsResponse]
})
编译失败,因为我缺少类型为 akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.scaladsl.model.ResponseEntity, com.mycompany.models.ItemsResponse]
的隐式解组器。
我不清楚使用 akka http 执行此操作的惯用方法是什么。我知道我可以使用 spray-json,但我想了解如何在不导入另一个库的情况下执行此操作。 Akka Http 似乎是可行的,但文档不清楚(至少对我而言)。
最简单的方法是使用 spray-json,因为它是 Akka HTTP 的一部分:
import spray.json._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
// change 2 to the number of attributes of ItemsResponse
implicit val ItemsResponseFormat = jsonFormat2(ItemsResponse)
这应该可以编译您现有的代码。
我认为你的问题是有效的,在某些情况下避免额外的依赖是有意义的。我的是制作一个身份验证库,我不想将我的 JSON 库首选项强加给此类库的用户。库需要 JSON 解组以理解令牌信息响应。
给代码! :)
case class TokenInfo private (uid: String, realm: String, scope: Seq[String])
object TokenInfo {
private
def parseOpt(s: String): Option[TokenInfo] = {
util.parsing.json.JSON.parseFull(s) match {
case Some(map: Map[String,Any] @unchecked) =>
val tmp: Map[String,Any] = map.collect {
case (k@ "uid",x: String) => k -> x
case (k@ "realm",x: String) => k -> x
case (k@ "scope",x: Seq[String] @unchecked) => k -> x
// other keys are ignored
}.toMap
if (tmp.size == 3) {
Some( TokenInfo( tmp("uid").asInstanceOf[String], tmp("realm").asInstanceOf[String], tmp("scope").asInstanceOf[Seq[String]]) )
} else {
None
}
case _ => None
}
}
implicit
val unm: FromEntityUnmarshaller[TokenInfo] = {
PredefinedFromEntityUnmarshallers.stringUnmarshaller.map{ s => parseOpt(s).getOrElse{
throw new RuntimeException(s"Unknown TokenInfo: $s")
}}
}
}
我选择使用 Scala 自带的 util.parsing.json
。另一个选项只是正则表达式,但在那种情况下,我要么修复字段的预期顺序,要么代码可能会变得复杂。