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
相关产品推荐
相关产品推荐

