播放如何为包含枚举的案例 class 实施隐式写入或格式

Play how to implement an implicit Writes or Format for a case class including an Enumeration

下面的 展示了如何使用 case class.

将枚举绑定到表单

但是在 Play 2.7.3 中,这段代码失败了:

No Json serializer found for type jura.SearchRequest. Try to implement an implicit Writes or Format for this type.

当我实现格式化程序时:

object SearchRequest {
  implicit val searchRequestFormat: OFormat[SearchRequest] = Json.format[SearchRequest]
}

我明白了

No instance of play.api.libs.json.Format is available for scala.Enumeration.Value in the implicit scope

我应该尝试为系统 scala.Enumeration 类型编写格式化程序吗?
或者当涉及枚举时,是否有另一种方法来实现格式化程序?

测试用例here.

要像cchantep说的那样将枚举写成字符串可以使用Writes.enumNameWrites,我们专门用来读写ID。因此我们在全局包中有一个 EnumFormat 用于枚举:

package object enums {

  implicit def reads[E <: Enumeration](enum: E): Reads[E#Value] = new Reads[E#Value] {
    def reads(json: JsValue): JsResult[E#Value] = json match {
      case JsNumber(s) =>
        try {
          JsSuccess(enum.apply(s.toInt))
        } catch {
          case _: NoSuchElementException => JsError(s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$s'")
        }
      case _ => JsError("Number value expected")
    }
  }

  implicit def writes[E <: Enumeration]: Writes[E#Value] = new Writes[E#Value] {
    def writes(v: E#Value): JsValue = JsNumber(v.id)
  }

  implicit def formatID[E <: Enumeration](enum: E): Format[E#Value] =
    Format(reads(enum), writes)


  def readsString[E <: Enumeration](enum: E): Reads[E#Value] = new Reads[E#Value] {
    def reads(json: JsValue): JsResult[E#Value] = json match {
      case JsString(s) => {
        try {
          JsSuccess(enum.withName(s))
        } catch {
          case _: NoSuchElementException => JsError(s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$s'")
        }
      }
      case _ => JsError("String value expected")
    }
  }

  implicit def writesString[E <: Enumeration]: Writes[E#Value] = new Writes[E#Value] {
    def writes(v: E#Value): JsValue = JsString(v.toString)
  }

  implicit def formatString[E <: Enumeration](enum: E): Format[E#Value] =
    Format(readsString(enum), writesString)
}

并使用:

object SearchRequest extends Enumeration(1) {
  type SearchRequest = Value

  val ONE /*1*/ , TWO /*2*/ , ETC /*n*/ = Value

  implicit val searchRequestFormat: Format[SearchRequest] = formatID(SearchRequest)
}

将以下内容添加到您的枚举对象

implicit object MatchFilterTypeFormatter extends Formatter[MatchFilterType.Value] {
    override val format = Some(("format.enum", Nil))
    override def bind(key: String, data: Map[String, String]) = {
      try {
        Right(MatchFilterType.withName(data.get(key).head))
      } catch {
        case e:NoSuchElementException =>  Left(Seq(play.api.data.FormError(key, "Invalid MatchFilterType Enumeration")))
      }
    }
    override def unbind(key: String, value: MatchFilterType.Value) = {
      Map(key -> value.toString)
    }
  }
  implicit val matchFilterTypeFormat = new Format[MatchFilterType.MatchFilterType] {
    def reads(json: JsValue) = JsSuccess(MatchFilterType.withName(json.as[String]))
    def writes(myEnum: MatchFilterType.MatchFilterType) = JsString(myEnum.toString)
  }

然后题中给出的Formatter/Controller就可以了

一个有效的测试用例是 here

我将此库用于任何枚举:enumeratum

使用 Dotty 将会有很棒的 Enumerations,但在那之前我想转向 enumeratum 是在 Scala 中处理枚举的最佳方式。另见 Dotty - Enumerations

作为奖励,有一个 play-json 扩展,请参阅 Play JSON Extension

有了这个,您的代码将如下所示:

import enumeratum.{ PlayJsonEnum, Enum, EnumEntry }

sealed trait SearchRequest extends EnumEntry

object SearchRequest extends Enum[SearchRequest] with PlayJsonEnum[SearchRequest] {

  val values = findValues

  case object SuperRequest  extends SearchRequest
  case object SimpleRequest extends SearchRequest
  ..
}

本质上,PlayJsonEnum[SearchRequest] 完成了所有工作。