如何使用 json4s 从 akka-http 响应实体读取 json 响应
How to read a json response from a akka-http response entity using json4s
我正在尝试调用 google 地理编码 api 并检索响应。
lazy val geoCodingConnectionFlow: Flow[HttpRequest, HttpResponse, Any] =
Http().outgoingConnectionHttps(config.getString("services.geoCodingApiHost"), config.getInt("services.geoCodingApiPort"))
def geoCodingRequest(request: HttpRequest): Future[HttpResponse] = Source.single(request).via(geoCodingConnectionFlow).runWith(Sink.head)
/**
* This call to google service is limited
* @see https://developers.google.com/maps/documentation/geocoding/#Limits
*/
def ?(l: GeoLocation)(implicit ec: ExecutionContext): Future[Either[String, List[Result]]] = {
val latlang = s"17.3644264,78.3896741"
import org.json4s.native.Serialization
import org.json4s.NoTypeHints
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling._
implicit val materializer = ActorMaterializer()
implicit val executor = system.dispatcher
implicit val formats = Serialization.formats(NoTypeHints)
geoCodingRequest(RequestBuilding.Get(s"${config.getString("services.geoCodingApiUrlPart")}?latlng=$latlang&key=${config.getString("services.geoCodingApiKey")}")).flatMap { response =>
val nonBinaryType = ContentTypes.`application/json`
def responseEntity: HttpEntity = response.entity
response.status match {
case OK if (response.entity.contentType == ContentTypes.`application/json`) => Unmarshal(response.entity).to[List[Result]].map(Right(_))
case BadRequest => Future.successful(Left(s"$latlang: incorrect Latitude and Longitude format"))
case _ => Unmarshal(response.entity).to[String].flatMap { entity =>
val error = s"Google GeoCoding request failed with status code ${response.status} and entity $entity"
Future.failed(new IOException(error))
}
}
}
}
}
我在尝试执行此命令时遇到以下编译错误!
Service.scala:78: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]
case OK if(response.entity.contentType == ContentTypes.`application/json`)=> Unmarshal(response.entity).to[List[Result]].map(Right(_))
请帮助我将结果解析为以下结果案例 类:
package com.thenewmotion.geocode
case class Address(
long_name: String,
short_name: String,
types: List[String]
)
case class Result(
address_components: List[Address],
formatted_address: String,
types: List[String]
)
case class Response(
results: List[Result],
status: Status
) {
def allResults = status match {
case Ok => Right(results)
case e: Error => Left(e)
}
}
/** @see https://developers.google.com/maps/documentation/geocoding/#StatusCodes */
sealed trait Status
case object Ok extends Status
sealed trait Error extends Status
case object ZeroResults extends Error
case object OverQuotaLimit extends Error
case object Denied extends Error
case object InvalidRequest extends Error
case class OtherError(description: String) extends Error
^
如您的错误消息中所述,您需要提供隐式 Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]
否则框架将不知道如何将响应实体转换为您的模型 List[com.thenewmotion.geocode.Result]
。
或者,您可以先使用内置的解组器将实体转换为字符串,然后使用 spray-json 将 json 字符串解析为目标模型:
import akka.http.scaladsl.unmarshalling.Unmarshal
import spray.json._
implicit val modelJsonReader = new JsonReader[List[com.thenewmotion.geocode.Result]] {
// See https://github.com/spray/spray-json on how to implement JsonReader
}
def parseJson(str: String): List[com.thenewmotion.geocode.Result] = {
// parse using spray-json
str.parseJson.convertTo[List[com.thenewmotion.geocode.Result]]
}
response.status match {
case OK if (response.entity.contentType == ContentTypes.`application/json`) =>
Unmarshal(response.entity).to[String].map { jsonString =>
Right(parseJson(jsonString))
}
case BadRequest => Future.successful(Left(s"$latlang: incorrect Latitude and Longitude format"))
case _ => Unmarshal(response.entity).to[String].flatMap { entity =>
val error = s"Google GeoCoding request failed with status code ${response.status} and entity $entity"
Future.failed(new IOException(error))
}
}
我正在尝试调用 google 地理编码 api 并检索响应。
lazy val geoCodingConnectionFlow: Flow[HttpRequest, HttpResponse, Any] =
Http().outgoingConnectionHttps(config.getString("services.geoCodingApiHost"), config.getInt("services.geoCodingApiPort"))
def geoCodingRequest(request: HttpRequest): Future[HttpResponse] = Source.single(request).via(geoCodingConnectionFlow).runWith(Sink.head)
/**
* This call to google service is limited
* @see https://developers.google.com/maps/documentation/geocoding/#Limits
*/
def ?(l: GeoLocation)(implicit ec: ExecutionContext): Future[Either[String, List[Result]]] = {
val latlang = s"17.3644264,78.3896741"
import org.json4s.native.Serialization
import org.json4s.NoTypeHints
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling._
implicit val materializer = ActorMaterializer()
implicit val executor = system.dispatcher
implicit val formats = Serialization.formats(NoTypeHints)
geoCodingRequest(RequestBuilding.Get(s"${config.getString("services.geoCodingApiUrlPart")}?latlng=$latlang&key=${config.getString("services.geoCodingApiKey")}")).flatMap { response =>
val nonBinaryType = ContentTypes.`application/json`
def responseEntity: HttpEntity = response.entity
response.status match {
case OK if (response.entity.contentType == ContentTypes.`application/json`) => Unmarshal(response.entity).to[List[Result]].map(Right(_))
case BadRequest => Future.successful(Left(s"$latlang: incorrect Latitude and Longitude format"))
case _ => Unmarshal(response.entity).to[String].flatMap { entity =>
val error = s"Google GeoCoding request failed with status code ${response.status} and entity $entity"
Future.failed(new IOException(error))
}
}
}
}
}
我在尝试执行此命令时遇到以下编译错误!
Service.scala:78: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]
case OK if(response.entity.contentType == ContentTypes.`application/json`)=> Unmarshal(response.entity).to[List[Result]].map(Right(_))
请帮助我将结果解析为以下结果案例 类:
package com.thenewmotion.geocode
case class Address(
long_name: String,
short_name: String,
types: List[String]
)
case class Result(
address_components: List[Address],
formatted_address: String,
types: List[String]
)
case class Response(
results: List[Result],
status: Status
) {
def allResults = status match {
case Ok => Right(results)
case e: Error => Left(e)
}
}
/** @see https://developers.google.com/maps/documentation/geocoding/#StatusCodes */
sealed trait Status
case object Ok extends Status
sealed trait Error extends Status
case object ZeroResults extends Error
case object OverQuotaLimit extends Error
case object Denied extends Error
case object InvalidRequest extends Error
case class OtherError(description: String) extends Error
^
如您的错误消息中所述,您需要提供隐式 Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]
否则框架将不知道如何将响应实体转换为您的模型 List[com.thenewmotion.geocode.Result]
。
或者,您可以先使用内置的解组器将实体转换为字符串,然后使用 spray-json 将 json 字符串解析为目标模型:
import akka.http.scaladsl.unmarshalling.Unmarshal
import spray.json._
implicit val modelJsonReader = new JsonReader[List[com.thenewmotion.geocode.Result]] {
// See https://github.com/spray/spray-json on how to implement JsonReader
}
def parseJson(str: String): List[com.thenewmotion.geocode.Result] = {
// parse using spray-json
str.parseJson.convertTo[List[com.thenewmotion.geocode.Result]]
}
response.status match {
case OK if (response.entity.contentType == ContentTypes.`application/json`) =>
Unmarshal(response.entity).to[String].map { jsonString =>
Right(parseJson(jsonString))
}
case BadRequest => Future.successful(Left(s"$latlang: incorrect Latitude and Longitude format"))
case _ => Unmarshal(response.entity).to[String].flatMap { entity =>
val error = s"Google GeoCoding request failed with status code ${response.status} and entity $entity"
Future.failed(new IOException(error))
}
}