scala 如何使用 json4s 从 akka-http 响应实体读取 json 响应

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35541738/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 08:02:12  来源:igfitidea点击:

How to read a json response from a akka-http response entity using json4s

jsonscalajson4sakka-http

提问by Phani

I am trying to invoke the google geocoding api and retrieve the response.

我正在尝试调用 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))
      }
    }
  }
}

}

}

I am getting the following compilation error when trying to execute this!

尝试执行此操作时出现以下编译错误!

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(_))

Please help me to get the result parsed into the following Result case classes:

请帮助我将结果解析为以下 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

                                                                                                                     ^

回答by PH88

As said in your error message, you need to provide an implicit Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]otherwise the framework won't know how to convert the response entity into your model List[com.thenewmotion.geocode.Result].

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

Alternatively, you can use the built in unmarshaller to convert the entity to String first, then use spray-json to parse the json string into the target model:

或者,您可以先使用内置的 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))
  }
}