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

Play Framework中如何在JSON数组内查找指定元素?

优雅解析异构JSON中指定元素的方案

问题场景

给定如下异构JSON数据:

{
  "name": "John",
  "placesVisited": [
    {
      "name": "Paris",
      "data": {
        "weather": "warm",
        "date": "31/01/22"
      }
    },
    {
      "name": "New York",
      "data": [
        {
          "weather": "warm",
          "date": "31/01/21"
        },
        {
          "weather": "cold",
          "date": "28/01/21"
        }
      ]
    }
  ]
}

其中placesVisited数组内元素的data类型不一致(Paris对应单个对象,New York对应对象数组),需要提取name为"New York"的元素并解析到对应的NewYorkModel case class,原实现方式不够优雅,需要优化。


优雅实现方案

1. 定义对应Case Class及JSON格式化器

首先定义解析所需的case class和Play JSON格式化器:

import play.api.libs.json._

case class WeatherRecord(weather: String, date: String)
case class NewYorkModel(name: String, data: List[WeatherRecord])

// 自动生成JSON序列化/反序列化逻辑
object WeatherRecord {
  implicit val format: OFormat[WeatherRecord] = Json.format[WeatherRecord]
}

object NewYorkModel {
  implicit val format: OFormat[NewYorkModel] = Json.format[NewYorkModel]
}

2. 类型安全的提取与解析逻辑

使用Play JSON的validate方法(而非as)实现类型安全的操作,同时通过Either优雅处理错误:

val jsonStr = """你的JSON字符串"""
val json = Json.parse(jsonStr)

// 用for推导式串联解析步骤,捕获所有可能的错误
val parseResult: Either[JsError, NewYorkModel] = for {
  // 先解析placesVisited为JsValue列表
  places <- (json \ "placesVisited").validate[List[JsValue]]
  // 筛选name为New York的元素,找不到则返回错误
  nyPlace <- places.find(place => (place \ "name").validate[String].contains("New York"))
    .toRight(JsError("未找到name为New York的条目"))
  // 将筛选出的元素解析为NewYorkModel
  nyModel <- nyPlace.validate[NewYorkModel]
} yield nyModel

// 处理解析结果
parseResult match {
  case Right(model) => 
    println(s"成功解析:${model.name},访问记录数:${model.data.size}")
  case Left(error) => 
    println(s"解析失败:${JsError.toJson(error)}")
}

3. 简洁版实现(适合对错误信息要求不高的场景)

如果不需要详细错误信息,也可以用asOpt实现更简洁的写法:

val nyModelOpt = (json \ "placesVisited")
  .asOpt[List[JsValue]]
  // 精准匹配name字段,避免toString.contains的脆弱逻辑
  .flatMap(_.find(place => (place \ "name").asOpt[String].contains("New York")))
  .flatMap(_.asOpt[NewYorkModel])

// 处理结果
val nyModel = nyModelOpt.getOrElse(throw new IllegalArgumentException("无法解析New York的访问记录"))

方案优势

  • 类型安全:用validate替代as,避免运行时崩溃,错误会被封装到Left中便于处理
  • 判断精准:直接解析name字段为String后做等值匹配,避免toString.contains可能出现的误匹配
  • 错误清晰:通过Either可以明确捕获「数组解析失败」「找不到目标元素」「模型解析失败」等不同错误
  • 代码简洁:for推导式串联操作,逻辑清晰易读

内容的提问来源于stack exchange,提问作者JohnBigs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:55:20