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

如何用Play框架解析含递归密封Trait列表的JSON?

Play框架下解析JSON到Scala密封特质对象的实现方案

针对你给出的Thing密封特质、Foo/Bar样例类,以及目标JSON结构,可以基于Play JSON库通过自定义Reads来实现解析,具体步骤如下:

1. 导入依赖包

确保项目已引入Play JSON库,在代码中导入核心类:

import play.api.libs.json._
import play.api.libs.functional.syntax._

2. 自定义各类型的Reads解析器

因为Thing是密封特质,且Foo对应JSON中的原始数字类型(而非对象),需要手动实现类型判断和解析逻辑:

(1)Foo的Reads

JSON中的Foo是单个整数,直接将Int映射为Foo实例:

implicit val fooReads: Reads[Foo] = Reads.of[Int].map(Foo)

(2)Thing的Reads(处理多态分支)

通过判断JSON节点的类型,区分是Foo还是Bar:

implicit lazy val thingReads: Reads[Thing] = Reads[Thing] {
  case JNumber(num) => fooReads.reads(JNumber(num))
  case obj: JObject => barReads.reads(obj)
  case other => JsError(s"无法解析为Thing类型:${other.toString()}")
}

这里用lazy val解决Thing和Bar之间的循环依赖(Bar的t字段是List[Thing])。

(3)Bar的Reads

Bar是包含s和t字段的对象,可选择两种实现方式:

  • 函数式语法组合(更灵活):
implicit val barReads: Reads[Bar] = (
  (__ \ "s").read[String] and
  (__ \ "t").read[List[Thing]]
)(Bar.apply)
  • 自动生成解析器(字段名与类属性完全匹配时可用):
implicit val barReads: Reads[Bar] = Json.reads[Bar]

3. 执行解析操作

注意你的原始JSON需要修正为标准JSON格式(键名必须加双引号),修正后的JSON如下:

{
  "s": "doomy doomy doom",
  "t": [ 24, { "s": "doooom!", "t": [ 1, 2, 3 ] }, 42, 126 ]
}

解析代码示例:

val jsonStr = """{"s": "doomy doomy doom","t": [ 24, { "s": "doooom!", "t": [ 1, 2, 3 ] }, 42, 126 ]}"""
val result: JsResult[Thing] = Json.parse(jsonStr).validate[Thing]

// 处理解析结果
result match {
  case JsSuccess(bar: Bar, _) => println(bar) // 输出目标Bar实例
  case JsError(errors) => println(s"解析失败:$errors")
}

关键说明

  • 必须使用标准JSON:原始JSON中的键名未加双引号,不符合JSON规范,Play JSON无法解析,需先修正。
  • 多态解析逻辑:通过判断JSON节点类型(JNumber/JObject)来分支处理Foo和Bar,确保不同结构的JSON能映射到对应的Scala类。
  • 循环依赖处理:Thing的Reads用lazy val延迟初始化,避免Bar依赖Thing导致的初始化顺序问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:23:23