使用 Scalaz 验证的错误累积
Error Accumulation with Scalaz Validation
我有一个复杂的 JSON,它保存在数据库中。它的复杂度是"segregated" in "blocks",如下:
整个JSON:
{
"block1" : {
"param1" : "val1",
"param2" : "val2"
},
"block2" : {
"param3" : "val3",
"param4" : "val4"
},
...
}
在数据库中,每个块都是单独存储和处理的:
持久块
"block1" : {
"param1" : "val1",
"param2" : "val2"
}
"block2" : {
"param3" : "val3",
"param4" : "val4"
}
每个块都有业务意义,因此,每个块都映射到一个案例-class。
我正在构建一个存储、更新和检索此 JSON 结构的 Play API,我想验证是否有人为了完整性而更改了它的数据。
我正在对每个块进行如下检索(解析和验证):
val block1 = Json.parse(block1).validate[Block1].get
val block2 = Json.parse(block2).validate[Block2].get
...
案例-class是:
trait Block
sealed case class Block1 (param1: String, param2: String, ...) extends Block
sealed case class Block2 (param3: String, param4: String, ...) extends Block
sealed case class Request (block1: Block1, block2: Block2, ...)
对于当前的结构,如果某些字段被更改并且与为其定义的类型不匹配,Play 将抛出此异常:
[NoSuchElementException: JsError.get]
所以,我想用 Scalaz 和 Validation 构建一个累积错误结构,以捕获所有可能的解析和验证错误。我已经看到 this and this 所以我用这种方式对验证进行了编码:
def build(block1: String, block2: String, ...): Validation[NonEmptyList[String], Request] = {
val block1 = Option(Json.parse(block1).validate[Block1].get).toSuccess("Error").toValidationNel
val block2 = Option(Json.parse(block2).validate[Block2].get).toSuccess("Error").toValidationNel
...
val request = (Request.apply _).curried
blockn <*> (... <*> (... <*> (...<*> (block2 <*> (block1 map request)))))
}
请注意,我使用的是应用函子 <*>
,因为 Request
有 20 个字段(使用该语法构建的括号是一团糟),|@|
应用函子仅适用对于 case classes 最多 12 个参数。
该代码适用于快乐路径,但是,当我修改某些字段时,Play 会抛出稍后描述的执行异常。
问题:我想累积Play在解析每个块时可以检测到的所有可能的结构错误。我该怎么做?
注意:如果Shapeless在某种程度上与此有关,我愿意使用它(我已经在使用它)。
如果多重验证是将 Scalaz 添加到您的项目的唯一原因,那么为什么不考虑一个替代方案,它不需要您将 Play 项目调整为 Scalaz 强制您解决问题的单子方式(这可能是一件好事,但如果唯一的原因是多重验证则不一定)。
而是考虑使用标准的 Scala 方法 scala.util.Try
:
val block1 = Try(Json.parse(block1).validate[Block1].get)
val block2 = Try(Json.parse(block2).validate[Block2].get)
...
val allBlocks : List[Try[Block]] = List(block1, block2, ...)
val failures : List[Failure[Block]] = allBlocks.collect { case f : Failure[Block] => f }
这样您仍然可以对标准 Scala 集合进行操作以检索要进一步处理的失败列表。此外,这种方法不会在块数方面限制您。
我有一个复杂的 JSON,它保存在数据库中。它的复杂度是"segregated" in "blocks",如下:
整个JSON:
{
"block1" : {
"param1" : "val1",
"param2" : "val2"
},
"block2" : {
"param3" : "val3",
"param4" : "val4"
},
...
}
在数据库中,每个块都是单独存储和处理的:
持久块
"block1" : {
"param1" : "val1",
"param2" : "val2"
}
"block2" : {
"param3" : "val3",
"param4" : "val4"
}
每个块都有业务意义,因此,每个块都映射到一个案例-class。 我正在构建一个存储、更新和检索此 JSON 结构的 Play API,我想验证是否有人为了完整性而更改了它的数据。
我正在对每个块进行如下检索(解析和验证):
val block1 = Json.parse(block1).validate[Block1].get
val block2 = Json.parse(block2).validate[Block2].get
...
案例-class是:
trait Block
sealed case class Block1 (param1: String, param2: String, ...) extends Block
sealed case class Block2 (param3: String, param4: String, ...) extends Block
sealed case class Request (block1: Block1, block2: Block2, ...)
对于当前的结构,如果某些字段被更改并且与为其定义的类型不匹配,Play 将抛出此异常:
[NoSuchElementException: JsError.get]
所以,我想用 Scalaz 和 Validation 构建一个累积错误结构,以捕获所有可能的解析和验证错误。我已经看到 this and this 所以我用这种方式对验证进行了编码:
def build(block1: String, block2: String, ...): Validation[NonEmptyList[String], Request] = {
val block1 = Option(Json.parse(block1).validate[Block1].get).toSuccess("Error").toValidationNel
val block2 = Option(Json.parse(block2).validate[Block2].get).toSuccess("Error").toValidationNel
...
val request = (Request.apply _).curried
blockn <*> (... <*> (... <*> (...<*> (block2 <*> (block1 map request)))))
}
请注意,我使用的是应用函子 <*>
,因为 Request
有 20 个字段(使用该语法构建的括号是一团糟),|@|
应用函子仅适用对于 case classes 最多 12 个参数。
该代码适用于快乐路径,但是,当我修改某些字段时,Play 会抛出稍后描述的执行异常。
问题:我想累积Play在解析每个块时可以检测到的所有可能的结构错误。我该怎么做?
注意:如果Shapeless在某种程度上与此有关,我愿意使用它(我已经在使用它)。
如果多重验证是将 Scalaz 添加到您的项目的唯一原因,那么为什么不考虑一个替代方案,它不需要您将 Play 项目调整为 Scalaz 强制您解决问题的单子方式(这可能是一件好事,但如果唯一的原因是多重验证则不一定)。
而是考虑使用标准的 Scala 方法 scala.util.Try
:
val block1 = Try(Json.parse(block1).validate[Block1].get)
val block2 = Try(Json.parse(block2).validate[Block2].get)
...
val allBlocks : List[Try[Block]] = List(block1, block2, ...)
val failures : List[Failure[Block]] = allBlocks.collect { case f : Failure[Block] => f }
这样您仍然可以对标准 Scala 集合进行操作以检索要进一步处理的失败列表。此外,这种方法不会在块数方面限制您。