如何在Scala中使用JsArray的5个元素?

时间:2015-04-17 22:24:44

标签: json scala

下面的代码会编译,但会抛出错误:Exception in thread "main" scala.MatchError:[{"id":6430758,"name":...] (of class play.api.libs.json.JsArray)。如何通过获取其中的items列表并且只有5个元素来读取给定链接的JSON?

import play.api.libs.json._

  def getProjects: List[Map[String, Any]] = {
    val iter = getJSON("https://api.github.com/search/repositories?q=scala")
    val json: JsValue = Json.parse(iter.get mkString "\n")
    val projects = (json \ "items") match {
      case l: List[Map[String, Any]] => l take 5
    }
    projects
  }

  def getJSON(url: String): Try[Iterator[String]] =
    Try(Source.fromURL(url).getLines) recover {
      case e: FileNotFoundException =>
        throw new AppException(s"Requested page does not exist: ${e.getMessage}.")
      case e: MalformedURLException =>
        throw new AppException(s"Please make sure to enter a valid URL: ${e.getMessage}.")
      case _ => throw new AppException("An unexpected error has occurred.")
    }

2 个答案:

答案 0 :(得分:2)

感觉快速而且肮脏,但如果这真的是你想做的事情,那么你可以尝试:

val listOfMaps: Seq[Map[String, String]] =
    (res1 \ "items").as[JsArray].value.map { jsobj =>
        jsobj.as[JsObject].value.map { case (key, value) =>
            key -> value.toString
        }
    }.take(5)

更好的选择是使用它们期望的键和类型创建一个case类,并编写一个Read来将Json解析为该case类。见https://www.playframework.com/documentation/2.3.x/ScalaJsonCombinators。然后你会有一个你的案例类列表,你可以从那里轻松拿5。

答案 1 :(得分:1)

由于您正在使用Play,因此您应该在其JsValue抽象内工作,而不是跳到Map[String, Any]

您的匹配失败的原因是因为json \ "items" 不是 Map[String, Any],而是JsValue。理想情况下,您知道JSON的结构(项目的架构是什么),您可以反序列化为:

case class Project(id: Long, name: String, ...)

object Project {
  implicit val fmt = Json.format[Project]
}

val projects = WS.get("https://api.github.com/search/repositories?q=scala").map { response =>
  response.json.validate[Map[String, Project]].map(_ take 5)
}

这会给你一个Future[JsResult[Map[String, Project]]]。外部类型为Future,因为该操作本质上是异步的,JsResult将是JsSuccessMap[String, Project]JsError包含原因的{{1}}您的JSON无法验证。