在spray-json中如何解析嵌套的json数据

3

我正在使用spray-client,希望使用spray-json(与之搭配)从谷歌地图API中获取一个经纬度对象。为此,我需要将整个响应结构设置好:

case class AddrComponent(long_name: String, short_name: String, types: List[String])
case class Location(lat: Double, lng: Double)
case class ViewPort(northeast: Location, southwest: Location)
case class Geometry(location: Location, location_type: String, viewport: ViewPort)
case class EachResult(address_components: List[AddrComponent],
                      formatted_address: String,
                      geometry: Geometry,
                      types: List[String])
case class GoogleApiResult[T](status: String, results: List[T])

object AddressProtocol extends DefaultJsonProtocol {
    implicit val addrFormat = jsonFormat3(AddrComponent)
    implicit val locFormat = jsonFormat2(Location)
    implicit val viewPortFormat = jsonFormat2(ViewPort)
    implicit val geomFormat = jsonFormat3(Geometry)
    implicit val eachResFormat = jsonFormat4(EachResult)
    implicit def GoogleApiFormat[T: JsonFormat] = jsonFormat2(GoogleApiResult.apply[T])
}
import AddressProtocol._

有没有办法从响应的json中只获取Location并避免所有这些冗长的代码?

spray-client 代码:

implicit val system = ActorSystem("test-system")
import system.dispatcher

private val pipeline = sendReceive ~> unmarshal[GoogleApiResult[EachResult]]

def getPostcode(postcode: String): Point = {
    val url = s"http://maps.googleapis.com/maps/api/geocode/json?address=$postcode,+UK&sensor=true"
    val future = pipeline(Get(url))
    val result = Await.result(future, 10 seconds)
    result.results.size match {
        case 0 => throw new PostcodeNotFoundException(postcode)
        case x if x > 1 => throw new MultipleResultsException(postcode)
        case _ => {
            val location = result.results(0).geometry.location
            new Point(location.lng, location.lat)
        }
    }
}

或者,我如何在spray-client中使用jackson?

4
你可以尝试使用 https://github.com/jrudolph/json-lenses/,它可以让你轻松地从 JSON 抽取数据。 - jrudolph
1
太棒了,需要稍微调整一下,但现在看起来很棒。这个会合并到spray-json中吗?应该的! - shmish111
将其与spray-json合并是计划,但我们目前没有能力进行移动。 - jrudolph
1个回答

2

在遵循jrudolph的建议使用json-lenses后,我进行了相当多的尝试,但最终使事情顺利运行。作为一个新手,我发现这很困难,而且我相信这个解决方案远非最优雅 - 尽管如此,我认为这可能会帮助人们或激发其他人进行改进。

给定JSON:

{
    "status": 200,
    "code": 0,
    "message": "",
    "payload": {
        "statuses": {
            "emailConfirmation": "PENDING",
            "phoneConfirmation": "DONE",
        }
    }
}

对于仅解析statuses的情况类,可以使用case class:

case class UserStatus(emailConfirmation: String, phoneConfirmation: String)

可以通过以下方式对响应进行反序列化:

import scala.concurrent.Future
import spray.http.HttpResponse
import spray.httpx.unmarshalling.{FromResponseUnmarshaller, MalformedContent}
import spray.json.DefaultJsonProtocol
import spray.json.lenses.JsonLenses._
import spray.client.pipelining._

object UserStatusJsonProtocol extends DefaultJsonProtocol {
  implicit val userStatusUnmarshaller = new FromResponseUnmarshaller[UserStatus] {
    implicit val userStatusJsonFormat = jsonFormat2(UserStatus)
    def apply(response: HttpResponse) = try {
      Right(response.entity.asString.extract[UserStatus]('payload / 'statuses))
    } catch { case x: Throwable =>
      Left(MalformedContent("Could not unmarshal user status.", x))
    }
  }
}
import UserStatusJsonProtocol._

def userStatus(userId: String): Future[UserStatus] = {
  val pipeline = sendReceive ~> unmarshal[UserStatus]
  pipeline(Get(s"/api/user/${userId}/status"))
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接