如何使用 scala 中的 playframework 将 json 反序列化为密封特征?

How to deserialize json to sealed trait using playframework in scala?

我是 Scala 的新手,被对象反序列化困住了。感谢您的帮助。

所以问题是,我有 sealed trait Permission 和一些扩展它的案例对象:

sealed trait Permission
case object Administrator extends Permission
case object Dispatcher extends Permission
case object Editor extends Permission
case object NormalUser extends Permission
object Permission {

  def valueOf(value: String): Permission = value match {
    case "Administrator"  => Administrator
    case "Dispatcher"     => Dispatcher
    case "Editor"         => Editor
    case "NormalUser"     => NormalUser
    case _                => throw new IllegalArgumentException()
  }

  def stringValueOf(value: Permission): String = value match {
    case Administrator  => "Administrator"
    case Dispatcher     => "Dispatcher"
    case Editor         => "Editor"
    case NormalUser     => "NormalUser"
    case _              => throw new IllegalArgumentException()
  }

}

我有 User 案例 class 和 Permission:

case class User(id: Option[Int],
                username: String,
                permission: Permission,
                firstName: Option[String]=None,
                lastName: Option[String]=None)

我已经创建了 Json.reads[Permission]Json.reads[User],但是每当我 运行 代码时,我都会得到 No unapply function found 异常。我试图搜索相同的问题,但没有得到任何结果。请帮助解决这个问题。谢谢

使用 scala 2.11.x 和 PlayFramework

Play 可以自动算出 JSON reader 仅适用于案例 类。在您的情况下,您必须手动为 Permission 定义 reader:

implicit val permissionReads: Reads[Permission] = 
  __.read[String].map(Permission.valueOf)

如果权限格式出现问题,这将抛出一个实际的异常,而不是返回 JsError。要解决此问题,您可以使用 collect:

implicit val permissionReads: Reads[Permission] = 
  __.read[String].collect(ValidationError("unsupported permission format"))(
    Function.unlift(s => scala.util.Try(Permission.valueOf(s)).toOption))

如果你不想要这么长的咒语 Function.unlift(s => scala.util.Try(Permission.valueOf(s)).toOption 你可以通过用 PartialFunction[String, Permission] 替换 valueOf(或向 object Permission 添加一个新函数)来减少它或返回 Option[Permission] 而不是抛出错误的函数。

经过一些研究和尝试,我得出了这个解决方案: implicit val permissionReads = (__).read[String].map{ permission => Permission.valueOf(permission)}

我来晚了一点,但看起来你正在使用 case objects,它已经有一个返回对象名称的 toString 方法,所以stringValueOf 在伴随方法中可能是多余的。

此外,如果您不想再维护该子类对象列表,您可能需要在此处查看我的库 https://github.com/lloydmeta/enumeratum,特别是 enumeratum-play 项目 JSON 内置序列化器和反序列化器。