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

Scala Play框架如何按属性值修改Json移除JsArray指定元素

问题描述

现有如下结构的JSON文件:

{
    "data": [
        {
            "id": 1001,
            "processes": [
                {
                    "process_id": 301,
                    "status": "accepted"
                },
                {
                    "process_id": 302,
                    "status": "accepted"
                },
                {
                    "process_id": 303,
                    "status": "failed"
                },
                {
                    "process_id": 304,
                    "status": "failed"
                }
            ]
        }
    ]
}

需求为遍历JSON结构,移除所有status属性值为failed的processes子元素,期望输出结构如下:

{
    "data": [
        {
            "id": 1001,
            "processes": [
                {
                    "process_id": 301,
                    "status": "accepted"
                },
                {
                    "process_id": 302,
                    "status": "accepted"
                }
            ]
        }
    ]
}

之前尝试使用ScalaJsonTransformers实现时,发现该组件的prune、update方法仅支持操作JsObject,无法直接作用于JsArray,先后尝试两种方案均未生效:

  • 方案1:编写过滤逻辑
    val inputJsonObj = Json.parse(inputJsonStr).as[Seq[JsObject]]
    val modifiedJson = inputJsonObj.map(model => (model \ "processes").as[Seq[JsObject]].filter(info => {
              val status = (info \ "status").as[String]
              status match {
                case "accepted" => true
                case _ =>
                  // 尝试在此处调用prune/update方法,无法生效
                false
              }
            }))
    
    该代码可以正确过滤出符合条件的元素,但无法回写到原始JSON结构中。
  • 方案2:创建可变JsObject覆写processes字段
    Json.parse(resp).as[mutable.Seq[JsObject]] ++ Json.obj("processes" -> modifiedJson)
    
    该方案同样无法达到预期效果。

要求:实现过程不使用case class。

实现方案

直接基于Play JSON的原生JsValue结构做层级修改即可,不需要依赖JsonTransformers的数组操作能力,核心逻辑是遍历data数组下的每个对象,取出processes数组做过滤后,重新组装成新的JsObject替换原字段,完整代码如下:

import play.api.libs.json._

// 解析原始JSON
val rawJson = Json.parse(inputJsonStr).as[JsObject]

// 处理data字段:遍历data数组内的每个元素,过滤processes数组
val updatedData = JsArray(
  (rawJson \ "data").as[Seq[JsObject]].map { dataItem =>
    // 过滤当前dataItem下的processes数组,只保留status=accepted的项
    val validProcesses = (dataItem \ "processes").as[Seq[JsObject]].filter { process =>
      (process \ "status").asOpt[String].contains("accepted")
    }
    // 覆写processes字段为过滤后的结果
    dataItem ++ Json.obj("processes" -> JsArray(validProcesses))
  }
)

// 组装最终结果
val resultJson = rawJson ++ Json.obj("data" -> updatedData)

// 如需输出为格式化字符串,使用如下代码
// val resultStr = Json.prettyPrint(resultJson)

逻辑说明

  • 之前两种方案失效的核心原因:过滤得到符合要求的processes列表后,没有将过滤结果回写到外层的data对象、根JSON结构中,仅单独拿到了过滤后的子列表,没有替换原结构的对应字段。
  • 代码中使用asOpt替代直接as做类型转换,可避免JSON结构不符合预期时直接抛出类型转换错误,容错性更好。
  • 全程直接操作JsValue/JsObject/JsArray原生JSON类型,无需定义任何case class,满足要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 21:24:20