【问题标题】:Error Accumulation with Scalaz ValidationScalaz 验证的错误累积
【发布时间】:2015-08-31 22:21:29
【问题描述】:

我有一个复杂的 JSON,它保存在数据库中。它的复杂性被“隔离”在“块”中,如下:

整个 JSON:

{
    "block1" : {
        "param1" : "val1",
        "param2" : "val2"
    },
    "block2" : {
        "param3" : "val3",
        "param4" : "val4"
    },
    ...
}

在数据库中,每个块都是单独存储和处理的:

持久化块

"block1" : {
    "param1" : "val1",
    "param2" : "val2"
}

"block2" : {
    "param3" : "val3",
    "param4" : "val4"
}

每个块都有业务意义,因此,每个块都映射到一个案例类。 我正在构建一个存储、更新和检索此 JSON 结构的 Play API,并且我想验证是否有人为了完整性而更改了它的数据。

我正在对每个块进行检索(解析和验证),如下所示:

val block1 = Json.parse(block1).validate[Block1].get
val block2 = Json.parse(block2).validate[Block2].get
...

案例类:

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 构建一个累积错误结构,以捕获所有可能的解析和验证错误。我见过thisthis 所以我用这种方式编码验证:

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)))))   
}

请注意,我正在使用应用函子 &lt;*&gt;,因为 Request 有 20 个字段(使用该语法构建括号混乱),|@| 应用函子仅适用于最多 12 个参数的案例类。

该代码适用于快乐路径,但是,当我修改某些字段时,Play 会引发稍后描述的执行异常。

问题:我想累积 Play 在解析每个块时可以检测到的所有可能的结构错误。我该怎么做?

注意:如果Shapeless 与此有关,我愿意使用它(我已经在使用它了)。

【问题讨论】:

    标签: json scala playframework scalaz play-json


    【解决方案1】:

    如果多重验证是将 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 集合进行操作,以检索要进一步处理的故障列表。此外,这种方法不会限制您的块数。

    【讨论】:

    • 为什么要否定呢?这不是对“问题:我想累积 Play 在解析每个块时可以检测到的所有可能的结构错误”的答案吗?我该怎么做?'?
    • 我不是对您的答案投反对票的人。实际上我认为你关于使用标准 Try 而不是 Scalaz 验证的想法是完全正确的,但我对此有一个疑问:你为什么说“......如果唯一的原因是多重验证”?如果我只使用验证,是否存在 Scalaz 的“潜在滥用”?
    • 我见过不少项目,其中 scalaz 主要用于验证功能,所以我猜它不能归类为“误用”。这里的问题在于陡峭的学习曲线和缺乏好的文档。 Scalaz 非常庞大且功能强大,考虑到未来的代码维护以及新开发人员的入职难易程度,将其融入系统的决定应该是有意识的。它还促进了某种编码和构造风格,这不一定是您在标准 Scala 中所做的。因此,我的回答提供了另一种选择:)
    • 好吧,我认为我已经“过度设计”了我的问题的解决方案。我会试试你的方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多