Scala 返回多个错误的验证

Scala 返回多个错误的验证,scala,Scala,我正在使用较旧的Scala版本(2.0)进行一些验证,但是对于每个记录,我目前只得到1个错误,假设该记录有2个或更多错误——我希望接收该记录/项的所有错误 case class Response(request: JsValue, success: Boolean, column: Option[String] = None, message: String = "") 这里是验证器,它返回json对象中的所有错误 def validator(asJson: JsVal

我正在使用较旧的Scala版本(2.0)进行一些验证,但是对于每个记录,我目前只得到1个错误,假设该记录有2个或更多错误——我希望接收该记录/项的所有错误

case class Response(request: JsValue,
    success: Boolean,
    column: Option[String] = None,
    message: String = "")
这里是验证器,它返回json对象中的所有错误

def validator(asJson: JsValue): Response = {
  if (name == "")
    return Response(asJson, false, Some("name"), "Name can't be blank")
  if (age == -1 || age == "N/A")
    return Response(asJson, false, Some("age"), "Age can't be blank")
  if (DOB == -1 || DOB == "N/A" )
    return Response(asJson, false, Some("DOB"), "DOB cannot be blank")
  else
    Response(asJson, true)
}
目前如果
record1
没有姓名+年龄+出生日期-->我只得到“姓名不能为空”

如何获取(每个项目有多个错误,而不是每个项目只有一个错误):


Name不能为空,Age不能为空,DOB不能为空

首先,在scala中没有
return
语句,最后一个语句被返回(事实上它存在,但不推荐使用)

其次,在您的代码中,我假设您希望传递几个
if
语句。但是,如果在每个
if
语句之后返回,则会在第一个条件为true时退出函数,并且永远不会转到代码的其余部分


如果要收集
响应的多个实例
,只需将它们收集到一个集合中,然后在函数末尾返回集合。集合可以是
列表
,或
序列
,或其他任何形式。

首先,在scala中,您没有
return
语句,最后一条语句被返回(事实上它存在,但不推荐使用)

其次,在您的代码中,我假设您希望传递几个
if
语句。但是,如果在每个
if
语句之后返回,则会在第一个条件为true时退出函数,并且永远不会转到代码的其余部分


如果要收集
响应的多个实例
,只需将它们收集到一个集合中,然后在函数末尾返回集合。集合可能是一个
列表
,或
序列
,或任何东西。

这只是它如何工作的概述:

def validator(asJson: JsValue): Response = {
  val errors = List(
    if (name == "" ) Some("Name can't be blank") else None,
    if (age == -1 || age == "N/A") Some("Age can't be blank") else None,
    if (DOB == -1 || DOB == "N/A" ) Some("DOB cannot be blank") else None,
  ).flatten

  if (errors.isEmpty) {
    Response(asJson, true)
  } else {
    Response(asJson, false, errors.mkString(", "))
  }
}
错误
值是通过对
选项
值进行
列表
创建的,每个验证检查一个值。
flatten
方法提取所有
Some
值的内容,并删除任何
None
值。结果是错误字符串的列表

其余代码根据错误列表格式化
响应


如果您使用的是Scala 2.13,则
选项。当
使测试更短、更清晰时:

Option.when(name == "")("Name can't be blank"),
Option.when(age == -1 || age == "N/A")("Age can't be blank"),
Option.when(DOB == -1 || DOB == "N/A")("DOB cannot be blank"),

这只是它如何工作的概述:

def validator(asJson: JsValue): Response = {
  val errors = List(
    if (name == "" ) Some("Name can't be blank") else None,
    if (age == -1 || age == "N/A") Some("Age can't be blank") else None,
    if (DOB == -1 || DOB == "N/A" ) Some("DOB cannot be blank") else None,
  ).flatten

  if (errors.isEmpty) {
    Response(asJson, true)
  } else {
    Response(asJson, false, errors.mkString(", "))
  }
}
错误
值是通过对
选项
值进行
列表
创建的,每个验证检查一个值。
flatten
方法提取所有
Some
值的内容,并删除任何
None
值。结果是错误字符串的列表

其余代码根据错误列表格式化
响应


如果您使用的是Scala 2.13,则
选项。当
使测试更短、更清晰时:

Option.when(name == "")("Name can't be blank"),
Option.when(age == -1 || age == "N/A")("Age can't be blank"),
Option.when(DOB == -1 || DOB == "N/A")("DOB cannot be blank"),

是在某个点

中使用的替代方案
import play.api.libs.json._
import cats.data.ValidatedNec
import cats.implicits._

case class User(name: String, age: Int, dob: String)
case class UserDTO(name: Option[String], age: Option[Int], dob: Option[String])
implicit val userDtoFormat = Json.format[UserDTO]
val raw =
  """
    |{
    |  "name": "Picard"
    |}
    |""".stripMargin
val userDto = Json.parse(raw).as[UserDTO]

def validateUser(userDto: UserDTO): ValidatedNec[String, User] = {
  def validateName(user: UserDTO): ValidatedNec[String, String] =
    user.name.map(_.validNec).getOrElse("Name is empty".invalidNec)

  def validateAge(user: UserDTO): ValidatedNec[String, Int] =
    user.age.map(_.validNec).getOrElse("Age is empty".invalidNec)

  def validateDob(user: UserDTO): ValidatedNec[String, String] =
    user.dob.map(_.validNec).getOrElse("DOB is empty".invalidNec)

  (validateName(userDto), validateAge(userDto), validateDob(userDto)).mapN(User)
}

validateUser(userDto)
// res0: cats.data.ValidatedNec[String,User] = Invalid(Chain(Age is empty, DOB is empty))
请注意
UserDTO
(“数据传输对象”)与
User
之间的区别,前者为通过线路发送的任何有效负载建模,后者为业务逻辑所需的实际数据建模。我们将用户验证问题分离到它自己的方法
validateUser
。现在我们可以处理有效用户或类似错误

validateUserDTO(userDto) match {
  case Valid(user) =>     
    // do something with valid user

  case Invalid(errors) => 
    // do something with errors
}

是在某个点

中使用的替代方案
import play.api.libs.json._
import cats.data.ValidatedNec
import cats.implicits._

case class User(name: String, age: Int, dob: String)
case class UserDTO(name: Option[String], age: Option[Int], dob: Option[String])
implicit val userDtoFormat = Json.format[UserDTO]
val raw =
  """
    |{
    |  "name": "Picard"
    |}
    |""".stripMargin
val userDto = Json.parse(raw).as[UserDTO]

def validateUser(userDto: UserDTO): ValidatedNec[String, User] = {
  def validateName(user: UserDTO): ValidatedNec[String, String] =
    user.name.map(_.validNec).getOrElse("Name is empty".invalidNec)

  def validateAge(user: UserDTO): ValidatedNec[String, Int] =
    user.age.map(_.validNec).getOrElse("Age is empty".invalidNec)

  def validateDob(user: UserDTO): ValidatedNec[String, String] =
    user.dob.map(_.validNec).getOrElse("DOB is empty".invalidNec)

  (validateName(userDto), validateAge(userDto), validateDob(userDto)).mapN(User)
}

validateUser(userDto)
// res0: cats.data.ValidatedNec[String,User] = Invalid(Chain(Age is empty, DOB is empty))
请注意
UserDTO
(“数据传输对象”)与
User
之间的区别,前者为通过线路发送的任何有效负载建模,后者为业务逻辑所需的实际数据建模。我们将用户验证问题分离到它自己的方法
validateUser
。现在我们可以处理有效用户或类似错误

validateUserDTO(userDto) match {
  case Valid(user) =>     
    // do something with valid user

  case Invalid(errors) => 
    // do something with errors
}

在我的代码中,我有一个简单的验证器,它可以做到这一点:

抽象类验证程序[T,ERR>:Null]{
def验证(a:T):顺序[错误]
受保护的定义为(errorCase:Boolean,err:=>err)=if(errorCase)err else null
受保护的def检查(检查:ERR*)=checks.collectFirst{case x if x!=null=>x}.getOrElse(null)
受保护的def checkAll(checks:ERR*)=checks.filter(!=null)
}
//对某些类型执行检查
val passwordValidator=新验证程序[字符串,字符串]{
val universalPasswordInstruction=“密码至少需要有一个小写字母、大写字母、数字和其他符号。”
def validate(a:String)=checkAll(//这里我们检查所有案例
是(a.长度<10,“密码应大于10”),
check(//这里我们只检查第一个。
是(a.forall(u.isleter),s“Passowrd只有字母.$universalPasswordInstruction”),
是(a.forall(u.IsleterOrdigit),s“使用至少一个ascii字符,如“#”或“*”.$universalPasswordInstruction”),
),
是(a.contains(“poop”),“真的!”)
)
}
val userValidator=新验证器[(字符串,字符串),(Int,字符串)]{
覆盖def validate(a:(字符串,字符串)):Seq[(Int,字符串)]=checkAll(
是(a._1.length>0,(0,“用户名为空!”),
)++passwordValidator.validate(a._2.map)(x=>(1,x))
}
//使用
userValidator.validate(“SomeUserName”->“SomePassword”)匹配{
case Seq()=>//确定大小写
案例错误=>//处理它
}

也许没那么花哨,但对我来说很管用:)。代码很简单,而且很容易解释。这里的空值只是实现细节(它们不会显示在用户代码中,可以切换到选项)。

在我的代码中,我有一个简单的验证器,它可以做到这一点:

抽象类验证程序[T,ERR>:Null]{
def验证(a:T):顺序[错误]
受保护的定义为(errorCase:Boolean,err:=>err)=if(errorCase)err else null
受保护的def检查(检查:ERR*)=checks.collectFirst{case x if x!=null=>x}.getOrElse(null)
受保护的def checkAll(checks:ERR*)=checks.filter(!=null)
}
//对某些类型执行检查
val passwordValida