下面的代码会编译,但会抛出错误: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.")
}
答案 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
将是JsSuccess
与Map[String, Project]
或JsError
包含原因的{{1}}您的JSON无法验证。