【问题标题】:What are the correct apply and unapply methods to avoid this java.lang.ClassCastException error in a Scala Play app?在 Scala Play 应用程序中避免此 java.lang.ClassCastException 错误的正确应用和取消应用方法是什么?
【发布时间】:2015-02-18 08:07:52
【问题描述】:

我正在构建一个 Scala Play 应用程序,其中事件和数据以 Json 格式保存,并且我正在尝试对用户和分配给他们的角色进行建模。我的想法是将 Roles 建模为案例对象,因为每个标准角色只需要为应用程序中的所有用户定义一次,我想对特定用户拥有的角色类型进行模式匹配被分配。到目前为止,我有这个;

package models

abstract class User {
  def displayName: String
  def role: Role
}

case class GuestUser(displayName: String, role: Role) extends User {
}

case class RegisteredUser(displayName: String, role: Role) extends User {
}

trait Role { // have tried abstract class too - but like idea of developing stackable traits for role permissions
}
object Role {
  implicit val RoleTypeFormat: Format[Role] = Json.format[Role]
  def apply(className: String): Role = Class.forName(className: String).asInstanceOf[Role]
  def unapply(role: Role): Option[String] = Option(this.getClass.getName) // have also tried .getSimpleName
}

case object GuestUserRole extends Role {
}

case object RegisteredUserRole extends Role {
}

如果我没有在 object Role 中定义 apply 和 unapply 方法,并且仅依赖于使用 Json.format[Role] 的隐式值,我会收到“未找到应用函数”或“未找到未应用函数”错误 -所以我添加了它们,试图摆脱这个错误。

如果不将 .asInstanceOf[Role] 添加到 Role 应用方法,我无法编译它。它现在可以编译,但是当我尝试使用新的 RegisteredUser 实例设置 role: Role 参数时,

val role: Role = RegisteredUserRole

创建了一个新的RegisteredUser 实例,其中角色属性被序列化为 Json;

"role":{"className":"models.Role$”}

但是当我尝试反序列化它时,我得到Exception in thread "pool-4868-thread-1" java.lang.ClassCastException: java.lang.Class cannot be cast to models.Role

我的目标是最终得到相同的 RegisteredUser 实例(或 GuestUser 实例),所以我可以在视图控制器中进行模式匹配,如下所示;

def isAuthorized: Boolean = { 
    role match { 
      case RegisteredUserRole => true
      case GuestUserRole => false
      // etc
    }
}

对此的任何帮助和建议将不胜感激。我在 Scala 和 Play 方面还不够熟练和知识渊博,无法知道我在建模用户和角色方面是否走在正确的轨道上。

【问题讨论】:

  • 我建议定义自定义 JSON 格式(即不使用 Json.format 快捷方式),而不是尝试添加 applyunapply 方法来使糖起作用。但是要回答您的实际问题,您不能将 Class 转换为对象实例,您需要使用反射 API 来获取其静态成员(其名称类似于 MODULE$),这将是实际实例。
  • 感谢您的推荐和回答@lmm - 我会探索这两种方法。

标签: scala playframework apply classcastexception unapply


【解决方案1】:

正如@lmm 建议的那样,最好提供一个自定义的Format[Role],而不是尝试以一种奇怪的方式创建实例。

类似这样的:

implicit val fmt = new Format[Role] {

    def reads(js: JsValue): JsResult[Role] = {
        js.validate[String] fold (
            error => JsError(error),
            role => role match {
                case "GuestUserRole" => JsSuccess(GuestUserRole)
                case "RegisteredUserRole" => JsSuccess(RegisteredUserRole)
                case _ => JsError(Nil) // Should probably contain some sort of `ValidationError`
            }
        )
    }

    def writes(r: Role): JsValue = JsString(r.toString)
}

【讨论】:

  • 非常感谢这个解决方案。它工作得很好:)(感谢@lmm 推荐这条路线)。我喜欢以直截了当和清晰的方式引用案例对象的事实。如果有的话,我需要很长时间才能从我目前的知识水平得出这个解决方案,所以再次感谢。我将更深入地研究 play.api.libs.json,以更好地理解自定义格式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-24
  • 2012-11-19
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多