2016-02-21 128 views
2

我試圖調用google地理編碼API並檢索響應。如何使用json4s從akka-http響應實體讀取json響應

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

請幫我拿到解析爲以下結果case類結果:

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 

                                ^

回答

5

如您的錯誤信息說,你需要提供一個隱含的Unmarshaller[akka.http.scaladsl.model.ResponseEntity,List[com.thenewmotion.geocode.Result]]否則框架將不知道如何將響應實體轉換爲您的模型List[com.thenewmotion.geocode.Result]

或者,您可以使用內置的解組到實體首先轉換爲字符串,然後用噴霧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)) 
    } 
}