2015-12-14 55 views
2

驗證JSON屬性與播放2.4

case class AclRuleScope(kind: String, value: String) 

,我想轉換JsonAclRuleScope有限制的其他財產的依賴性:

type可能只有:「默認」 | 「用戶」| 「組」| 「domain」

value如果type爲「user」,則可能只有電子郵件| 「羣」,而在另一個案件

我有對象與ReaderWriter,但我不能明白,我怎麼能得到type值一些字符串讀value時:

object AclRuleScope { 

    implicit val aclRuleScopeRead = (
    (__ \ "type").read[String](pattern("^(default|user|group|domain)$".r)) and 
     (__ \ "value").read[String](
      email keepAnd 
      filter(
       ValidationError("error.scope.value") 
     )(??? == JsString("user") || ??? == JsString("group"))) 
    )(this.apply _) 

} 

必須是什麼???

回答

1

JsConstraints#filter具有以下特徵

def filter[A](otherwise: ValidationError)(p: A => Boolean)(implicit reads: Reads[A]) 

,當你寫

filter(ValidationError("error.scope.value"))(??? == JsString("user") || ??? == JsString("group"))) 

代碼(??? == JsString("user") || ??? == JsString("group"))其實是過濾第二個參數,因此應當是A => Boolean謂語。此外,由於這是在email讀數爲Reads[String]之後應用的,所以您的實際AString,因此您應該丟棄JsString

你可以寫最小的變化是:

implicit val aclRuleScopeRead = (
    (__ \ "type").read[String](pattern("^(default|user|group|domain)$".r)) and 
    (__ \ "value").read[String](
     email keepAnd 
     filter(
      ValidationError("error.scope.value") 
     )(x => x == "user" || x == "group")) 
).tupled 

我強烈建議您謂詞提取到它自己的方法:

def isValidEmail: (String) => Boolean = { 
    x => x == "user" || x == "group" 
} 

,寫你的行文

implicit val aclRuleScopeRead = (
    (__ \ "type").read[String](pattern("^(default|user|group|domain)$".r)) and 
    (__ \ "value").read[String](
     email keepAnd filter(ValidationError("error.scope.value"))(isValidEmail)) 
).tupled 

更好,你可以有

val validEmail = email keepAnd filter(ValidationError("error.scope.value"))(isValidEmail)) 

,寫

implicit val aclRuleScopeRead = (
    (__ \ "type").read[String](pattern("^(default|user|group|domain)$".r)) and 
    (__ \ "value").read[String](validEmail) 
).tupled 

在澄清的意見,你只希望如果類型是「用戶」或「集團」返回一個空字符串,如果不是這樣解析電子郵件。

答案是接近this question

概述的溶液中value場的讀取首先需要檢查type字段的值。類型字段上的條件如下所示:

(__ \「type」)。read [String] .filter(ValidationError(「error.scope。值「))(isEmailType)

其中isEmailType被定義爲

def isEmailType: (String) => Boolean = { x => x == "user" || x == "group" } 

這將返回一個讀,給出了一個JsSuccess如果類型是usergroupJsError否則,從評論,我們知道,我們應該返回空字符串,如果該類型不是usergroup,在讀可以成爲:

(__ \ "type").read[String] 
      .filter(ValidationError("error.scope.value"))(isEmailType) 
      .orElse Reads.pure("") 

是一種安全,絕不會返回一個JsError。這很好,因爲有專門的reads來強制type驗證,我們目前操作的讀取僅作爲驗證的一部分value。 現在,我們需要改變讀取解析value如果解析是JsSuccess

(__ \ "type").read[String] 
      .filter(ValidationError("error.scope.value"))(isEmailType) 
      .flatMap(_ => (__ \ "value").read[String](email)) 
      .orElse Reads.pure("") 

使用flatMap,我們更換type讀取由上value正確讀取如果type讀取是成功的。

+0

validEmail將測試(__ \ 「值」)?因爲「用戶」||,驗證將是錯誤的「組」不是有效的電子郵件。如果(__ \「type」)等於「user」||,我想測試(__ \「value」)是僅限電子郵件「組」。我需要'???'作爲(__ \「type」)的值 –

+0

噢好吧抱歉,我想我明白你的意思是它將會變長:)如果類型是別的東西,只要一個字符串? – Jean

+0

for'default'type - 空字符串,'domain'類型 - (__ \「value」)值。其他類型受(__ \「type」)讀取限制。 –

0

謝謝@Jean,你指着我朝着正確的方向前進。

flatMapfilter幫助我,和我有

case class AclRuleScope(kind: String, value: Option[String]) 

val aclRuleScopeRead = (
    (__ \ "type").read[String] and 
    (__ \ "type").read[String](pattern("^(default|user|group|domain)$".r)).flatMap { 
     case t if "user".equals(t) || "group".equals(t) => 
     (__ \ "value").readNullable[String](email) 
      .filter(ValidationError(s"error.acl.scope.value omitted for '$t' type"))(_.isDefined) 
      .filter(ValidationError(s"error.acl.scope.value not defained for '$t' type"))(_.exists(_.nonEmpty)) 
     case "domain" => 
     (__ \ "value").readNullable[String] 
      .filter(ValidationError("error.acl.scope.value omitted for 'domain' type"))(_.isDefined) 
      .filter(ValidationError("error.acl.scope.value not defained for 'domain' type"))(_.exists(_.nonEmpty)) 
     case "default" => 
     (__ \ "value").readNullable[String] 
      .filter(ValidationError("error.acl.scope.value must be omitted for 'default' type"))(_.isEmpty) 
    } 
)(AclRuleScope.apply _)