【问题标题】:Play: JSON Writes is not respected when class extends case class播放:当类扩展案例类时,不尊重 JSON 写入
【发布时间】:2025-12-06 18:40:01
【问题描述】:

我有以下类层次结构

case class A(id: String,name:String)
object A {
   implicit val reads:Reads[A] = Json.reads[A]
   implicit val writes:Writes[A] = Json.writes[A]
}

class B(id:String,name:String, val other:Int,val another:Vector[String]) extends A(id,name)
object B {
   implicit val reads:Reads[B] = Json.reads[B]
   implicit val writes:Writes[B] = Json.writes[B]

   def apply(id: String,name:String, other:Int, another:Vector[String]) = new B(id,name,other,another)
   def unapply(b: B):Option[(String,String)] = Some {(b.id,b.name,b.other,b.another)} 

}

序列化B时的结果只包含其父类A的状态,如下:

{
  id:"some value",
  name: "some other value"
}

ReadsWrites 的配置应该是什么,以便尊重子类中的配置,并适当地序列化所有包含的键值对。我期望的结果如下:

{
  id:"some value",
  name: "some other value",
  other: 4,
  another: ["a","b"]
}

我不想对键值对进行花哨的自定义序列化。如果编译器没有抱怨父类缺少读写隐式,我只会在子类 B 中包含读写操作。

【问题讨论】:

  • 通常不鼓励扩展案例类,因为它会导致一些奇怪的行为。尝试为您的基类使用特征或抽象类。
  • 我不能使用特征,因为我需要参数。如果我使用抽象类,我需要根据 Play 的约定创建一个 apply 和 unapply 方法。为了创建一个应用方法,我需要某种构造函数。我应该在抽象类中使用什么样的构造函数?它是抽象的。
  • 来自 Play 的文档:宏适用于满足以下要求的类和特征。 * 它必须有一个具有 apply 和 unapply 方法的伴生对象。 * unapply 的返回类型必须与 apply 方法的参数类型匹配。 * apply 方法的参数名称必须与 JSON 中所需的属性名称相同。
  • 您的代码无法为我编译,因为Json.reads[B]already 抱怨没有应用方法。此外,如果您只调用Json.toJson(new B(...)),则隐式解析将是模棱两可的。抱歉,我可能不明白你希望你的代码最终是什么样子。
  • 你是对的。在尝试调整原始代码时,我删除了 apply 和 unapply 方法。

标签: scala playframework play-json


【解决方案1】:

只需像这样使用this

sealed trait A

object A {
  implicit val oformat: OFormat[A] = julienrf.json.derived.oformat(adapter = NameAdapter.snakeCase)
}

case class B(x: String, y: String) extends A {}

而且一切都很顺利...老实说,Play 使用 JSON 的方法仍然非常原始 - 例如与 Spring 相比。

【讨论】: