播放 JSON 组合器验证至少指定了一个字段

Posted

技术标签:

【中文标题】播放 JSON 组合器验证至少指定了一个字段【英文标题】:Play JSON combinator validates at least a field is specified 【发布时间】:2015-08-19 09:17:20 【问题描述】:

我有一个场景,当将 json 解析为这样的案例类时

implicit val userRead: Reads[User] = (
    (__ \ "name").read[String] ~
      (__ \ "email").readNullable[String] ~
      (__ \ "phone").readNullable[String] ~
      Reads.pure(None)
    )(User.apply _)

我不需要emailphone 都可用,但至少其中一个必须可用。

在我的案例类定义中,我可以防止它们都为空的情况

case class User(name: String, email: Option[String], phone: Option[String], id: Option[Long] = None) 
  require(email.nonEmpty || phone.nonEmpty, "must have at least an email or phone number")

但是,这样做会产生异常,并响应 500 状态,而由于用户输入,这应该是 400 错误。

我当然可以在控制器中手动执行验证,但我想知道是否有更简洁的方法来执行此操作。

【问题讨论】:

【参考方案1】:

我只能建议您手动编写自己的“读取”

case class A(i: Option[Int], j: Option[Int])

implicit val reads: Reads[A] = new Reads[A]  
  override def reads(json: JsValue): JsResult[A] =  
    (for 
      i <- (json \ "i").validateOpt[Int] 
      j <-  (json \ "j").validateOpt[Int]
      yield A(i, j))
       .filter(JsError("both values can't be empty"))(a ⇒ a.j.nonEmpty || a.i.nonEmpty)  
     
  

然后进行测试:

scala> Json.parse(""" "i" : 1 """).validate[A]
res4: play.api.libs.json.JsResult[A] = JsSuccess(A(Some(1),None),)


scala> Json.parse(""" """).validate[A]
res5: play.api.libs.json.JsResult[A] = JsError(List((,List(ValidationError(List(from and to in range can't be both empty),WrappedArray())))))

【讨论】:

感谢 Tyth,至少这样我就不必验证所有代码了。【参考方案2】:

您只需对现有代码 (.filter(ValidationError("must have at least an email or phone number"))(u =&gt; u.email.isDefined || u.phone.isDefined)) 进行少量添加即可实现此目的:

case class User(name: String, email: Option[String], phone: Option[String], id: Option[Long] = None)

implicit val userRead: Reads[User] = (
    (__ \ "name").read[String] ~
    (__ \ "email").readNullable[String] ~
    (__ \ "phone").readNullable[String] ~
    Reads.pure(None)
  )(User.apply _).filter(ValidationError("must have at least an email or phone number"))(u => u.email.isDefined || u.phone.isDefined)

现在,证明这是可行的:

scala> Json.parse(""" "name" : "foobar" """).validate[User]
res3: play.api.libs.json.JsResult[User] = JsError(List((,List(ValidationError(List(must have at least an email or phone number),WrappedArray())))))

scala> Json.parse(""" "name" : "foobar", "email":"test" """).validate[User]
res4: play.api.libs.json.JsResult[User] = JsSuccess(User(foobar,Some(test),None,None),)

scala> Json.parse(""" "name" : "foobar", "phone":"test" """).validate[User]
res5: play.api.libs.json.JsResult[User] = JsSuccess(User(foobar,None,Some(test),None),)

【讨论】:

以上是关于播放 JSON 组合器验证至少指定了一个字段的主要内容,如果未能解决你的问题,请参考以下文章

Laravel“至少一个”字段需要验证

验证WCF Rest Service中Json对象的结构

从 extjs 中的表单字段验证和获取 JSON 文件(所有客户端,无服务器)中的数据。绝对的菜鸟

日期选择器的 Kendo 自定义验证

大小写字母,特殊字符,数字,四选一组合或者全组合,长度至少八位,验证

最简单的 JQuery 验证规则示例