在 Play Framework 2.3.x (Scala) 中为案例 class 定义交叉属性 Json 验证器

Defining cross-attribute Json Validators for a case class in Play Framework 2.3.x (Scala)

我知道可以定义我们想要匹配给定 json 的案例 classes(我们使用 JsValue.validate[T]):

例如:

case class UpdateDashboardModel(id: Long,
                            maybeName: Option[String],
                            containers: Option[List[UpdateContainerModel]],
                            description: Option[String])

然后我们必须编写一个 Reads[T] 来定义如何将一个 json 对象实际转换为我们案例的一个实例 class(并可选择地为个人定义一些自定义验证器属性):

  val exists: Reads[Long] =
    Reads.LongReads.filter(ValidationError("Dashboard does not exist"))(long => Dashboard.findById(long).isDefined)


  implicit val reads: Reads[UpdateDashboardModel] = (
    (JsPath \ "id").read[Long](exists) and
    (JsPath \ "name").readNullable[String] and
    (JsPath \ "containers").readNullable[List[UpdateContainerModel]] and
    (JsPath \ "description").readNullable[String]) (UpdateDashboardModel.apply _ )

在此示例中,我 运行 对给定的 id 进行了简单验证 -> 它必须存在于数据库中,否则我必须抛出错误。

问题是,我似乎无法为需要 两个 属性的东西编写验证程序。

例如,我想编写一个简短的验证器,它采用 id and name 属性,因为我想检查该名称是否尚未被另一个仪表板使用(如果它是当前仪表板,则可以)。

有人能想办法做到这一点吗?

提前致谢。

好吧,它不是很漂亮,但我认为这样的事情应该可行...让我们假设 isNameAvailable 是一个确保该名称尚未被另一个仪表板使用的函数,并且 returns 如果名称可用则为真(否则为假)。

import play.api.data.validation.ValidationError

implicit val reads: Reads[UpdateDashboardModel] = (
  (JsPath \ "id").read[Long](exists) and
  (
    (JsPath \ "id").read[Long] and
    (JsPath \ "name").readNullable[String]
  ).tupled.filter(
    ValidationError("Name is already in use")
  )
  { case (id, name) => isNameAvailable(name, id) }.map(t => t._2) and
  (JsPath \ "containers").readNullable[List[UpdateContainerModel]] and
  (JsPath \ "description").readNullable[String]) (UpdateDashboardModel.apply _ )

就我个人而言,我发现 tupled.filter 语法有点奇怪,但这就是我能够克服您遇到的问题的方法。