如何在Scala Play Framework中解析JSON数组并提取指定字段值
Play JSON 遍历查询实现方案
前置说明
Play框架内置play-json库专门用于JSON处理,无需额外引入依赖,非Play独立项目可在sbt中添加依赖:
"com.typesafe.play" %% "play-json" % "2.9.4" // 版本可匹配你使用的Play版本调整
方案1:直接遍历JsValue实现
无需定义数据结构,直接操作JSON节点:
import play.api.libs.json._ // 你的JSON字符串 val jsonStr: String = """[ { "events": [ { "type": "message", "attributes": [ {"key": "action","value": "withdraw_reward"}, {"key": "sender","value": "bob"}, {"key": "module","value": "distribution"}, {"key": "sender","value": "bob"} ] }, { "type": "credit", "attributes": [ {"key": "recipient","value": "ross"}, {"key": "sender","value": "bob"}, {"key": "amount","value": "100"} ] }, { "type": "rewards", "attributes": [ {"key": "amount","value": "100"}, {"key": "validator","value": "sarah"} ] } ] }, { "events": [ { "type": "message", "attributes": [ {"key": "action","value": "withdraw_reward"}, {"key": "sender","value": "bob"}, {"key": "module","value": "distribution"}, {"key": "sender","value": "bob"} ] }, { "type": "credit", "attributes": [ {"key": "recipient","value": "ross"}, {"key": "sender","value": "bob"}, {"key": "amount","value": "100"} ] }, { "type": "rewards", "attributes": [ {"key": "amount","value": "200"}, {"key": "validator","value": "Ryan"} ] } ] } ]""" // 解析字符串为JsValue val json: JsValue = Json.parse(jsonStr) // 核心查询逻辑 val sarahRewardAmount: Option[String] = json.as[JsArray].value // 提取所有根节点下的events .flatMap(rootItem => (rootItem \ "events").asOpt[JsArray].getOrElse(JsArray.empty).value) // 过滤type为rewards的事件 .filter(event => (event \ "type").asOpt[String].contains("rewards")) // 匹配validator为sarah的记录,提取amount .flatMap(rewardEvent => { // 将attributes转为键值对Map方便查找 val attrMap = (rewardEvent \ "attributes").asOpt[JsArray].getOrElse(JsArray.empty).value .flatMap(attr => for { key <- (attr \ "key").asOpt[String] value <- (attr \ "value").asOpt[String] } yield key -> value).toMap if (attrMap.get("validator").contains("sarah")) attrMap.get("amount") else None }) // 取第一条符合条件的结果,去掉headOption可获取所有符合条件的集合 .headOption // 输出结果:Some("100") println(sarahRewardAmount)
所有节点取值用asOpt而非as,避免JSON结构不匹配时抛出异常,返回Option类型保证安全。
方案2:样例类反序列化实现(更推荐)
定义对应结构的样例类,利用Play JSON自动反序列化,代码更简洁、类型安全:
import play.api.libs.json._ // 定义和JSON结构匹配的样例类 case class Attribute(key: String, value: String) case class Event(`type`: String, attributes: Seq[Attribute]) case class RootItem(events: Seq[Event]) // 隐式反序列化规则,Play JSON自动生成 implicit val attrReads: Reads[Attribute] = Json.reads[Attribute] implicit val eventReads: Reads[Event] = Json.reads[Event] implicit val rootReads: Reads[RootItem] = Json.reads[RootItem] // 解析JSON为样例类集合 val items: Seq[RootItem] = json.as[Seq[RootItem]] // 查询逻辑 val sarahRewardAmount: Option[String] = items .flatMap(_.events) .filter(_.`type` == "rewards") .flatMap(rewardEvent => { val attrMap = rewardEvent.attributes.map(attr => attr.key -> attr.value).toMap if (attrMap.get("validator").contains("sarah")) attrMap.get("amount") else None }) .headOption
结果说明
两种方案最终得到的结果都是Some("100"),如果没有符合条件的记录则返回None,如果需要转为数值类型可调用.map(_.toInt)转换。
内容的提问来源于stack exchange,提问作者bahdotsh
相关产品推荐
相关产品推荐

