2016-02-21 143 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)) 
    } 
}