Play Framework中Scala 3枚举的JSON与表单绑定实现问题
解决Scala 3枚举在Play Framework表单与JSON中的适配问题
核心问题拆解
你遇到的报错本质是Play表单系统找不到Frequency枚举对应的Formatter类型类——这和JSON序列化是两套独立机制,你之前尝试的EnumFormat、Json.formatEnum只解决JSON读写,表单绑定需要单独实现Formatter[Frequency]。另外代码里还有两处明显问题:
case class A手动定义的unapply完全错误,case class会自动生成正确的unapply方法,无需手写- 枚举内的implicit定义位置不合理,应该放在枚举的伴生对象中,确保作用域可访问
分步解决方案
1. 修正case class A的表单定义
移除错误的手动unapply,将表单定义放在伴生对象中更规范:
case class A(freq: Frequency) object A { val form: Form[A] = Form( mapping( "freq" -> of[Frequency] )(A.apply)(A.unapply) ) }
2. 为Scala 3枚举实现表单Formatter
Play表单依赖Formatter[T]完成字符串与枚举值的双向转换,为Frequency添加implicit实现:
import play.api.data.FormError import play.api.data.format.Formatter enum Frequency { case None, Daily, Weekly } object Frequency { // 表单绑定用Formatter:处理字符串转枚举、枚举转字符串 implicit val frequencyFormatter: Formatter[Frequency] = new Formatter[Frequency] { override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], Frequency] = { data.get(key) match { // 用枚举的values方法匹配,避免硬编码每个case case Some(name) => Frequency.values.find(_.toString == name) .toRight(Seq(FormError(key, s"无效的频率值: $name", Nil))) case None => Left(Seq(FormError(key, "频率为必填项", Nil))) } } override def unbind(key: String, value: Frequency): Map[String, String] = { Map(key -> value.toString) } } }
3. 简化JSON序列化(可选)
如果使用Play JSON 2.10+版本,Scala 3枚举无需额外依赖第三方工具,直接用官方API即可:
import play.api.libs.json._ object Frequency { // 接上面的Formatter代码,添加JSON Format implicit val frequencyFormat: OFormat[Frequency] = Json.formatEnum(Frequency) // 也可以用派生语法更简洁(需要导入AutoFormat) // implicit val frequencyFormat: OFormat[Frequency] = DeriveOFormat.gen[Frequency] }
完整可运行代码示例
import play.api.data.Form import play.api.data.FormError import play.api.data.format.Formatter import play.api.data.mapping import play.api.libs.json._ enum Frequency { case None, Daily, Weekly } object Frequency { // 表单绑定Formatter implicit val frequencyFormatter: Formatter[Frequency] = new Formatter[Frequency] { override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], Frequency] = { data.get(key) match { case Some(name) => Frequency.values.find(_.toString == name) .toRight(Seq(FormError(key, s"无效的频率值: $name", Nil))) case None => Left(Seq(FormError(key, "频率为必填项", Nil))) } } override def unbind(key: String, value: Frequency): Map[String, String] = { Map(key -> value.toString) } } // JSON序列化Format implicit val frequencyFormat: OFormat[Frequency] = Json.formatEnum(Frequency) } case class A(freq: Frequency) object A { val form: Form[A] = Form( mapping( "freq" -> of[Frequency] )(A.apply)(A.unapply) ) }
关键注意事项
- 表单绑定用
Formatter[T],JSON序列化用Format[T],二者是完全独立的类型类,不要混淆 - Scala 3枚举自带
values方法,可以获取所有枚举实例,用来简化字符串匹配逻辑 - 所有implicit定义放在枚举的伴生对象中,确保在表单、JSON序列化的作用域内能够被自动找到
内容的提问来源于stack exchange,提问作者Raj Malhotra
相关产品推荐
相关产品推荐

