【问题标题】:How to read a json response from a akka-http response entity using json4s如何使用 json4s 从 akka-http 响应实体读取 json 响应
【发布时间】:2016-06-03 04:14:32
【问题描述】:

我正在尝试调用 google geocoding 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(_))

请帮助我将结果解析为以下 Result 案例类:

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

                                                                                                                     ^

【问题讨论】:

    标签: json scala json4s akka-http


    【解决方案1】:

    正如您在错误消息中所说,您需要提供隐式Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]],否则框架将不知道如何将响应实体转换为您的模型List[com.thenewmotion.geocode.Result]

    或者,您可以使用内置的 unmarshaller 先将实体转换为 String,然后使用 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))
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-13
      • 1970-01-01
      • 2022-06-28
      • 2014-01-16
      • 1970-01-01
      • 1970-01-01
      • 2014-04-15
      • 1970-01-01
      相关资源
      最近更新 更多