You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 17:39:03