首页 > 解决方案 > 在使用 scala 的版本化配置的情况下,如何将解析与验证分开?

问题描述

背景

我有一组配置 JSON 文件,如下所示:

{
  "version" : 1.0,
  "startDate": 1548419535,
  "endDate": 1558419535,
  "sourceData" : [...]  // nested json inside the List.
  "destData" : [...]    // nested json inside the List.
  "extra" : ["business_type"]
}

有几个这样的配置文件。它们是固定的并且仅驻留在我的代码目录中。每个配置文件的内部表示由我的案例类给出Config

case class Attribute(name: String, mappedTo: String)

case class Data(location: String, mappings:List[Attribute])

case class Config(version: Double, startDate: Long, endDate: Long, sourceData: List[Data],
                  destData: List[Data], extra: List[String])

我有三个班ProviderParserValidator

  1. Provider有方法getConfig(date: Long): Config。它必须返回令人满意的配置startDate <= date <= endDate(理想情况下,应该存在一个这样的配置,startDateendDate定义要返回的配置版本)。
  2. getConfig调用内部的一个方法Parsercalled parseList(jsonConfigs: List[String]): Try[List[Config]]。什么parseList是尝试反序列化列表中的所有配置,每个配置为 case class 的一个实例Config。即使一个 JSON 无法反序列化,它也会parseList返回 a ,scala.util.Failure否则它会返回scala.util.Success[List[Config]]
  3. 如果scala.util.Success[List[Config]]是上一步返回的,那么最后会调用getConfig被调用的方法,并返回它的结果。由于我希望累积所有错误,因此我使用Cats Validated进行验证。我什至在这里问了一个关于它的正确用法的问题Validatordef validate(List[Config], Date): ValidationResult[Config]
  4. validate执行以下操作:检查列表中是否恰好有一个 Config适用于给定日期(startDate <= date <= endDate),然后对其执行一些验证Config(否则它返回一个invalidNel)。我执行一些基本验证,Config例如检查各种列表和字符串是否为非空等。我还执行一些语义验证,例如检查字段中的每个字符串extra是否存在于mappings每个source/dest Data等中。

问题

  1. 最近几天困扰我的问题是,我使用的目的Cats Validated仅仅是收集所有错误(而不是在遇到第一个验证错误时快速失败)。但是当我到达validate方法时,我已经在parseList方法中进行了某种验证。也就是说,我已经验证了parseList我的 JSON 结构符合我的案例类Config。但我parseList不会像我的validate方法那样积累错误。因此,如果我的 json 结构和我的案例类Config之间存在许多不兼容性,我只会了解第一个。但我想同时了解它们。
  2. 如果我开始只在案例类中添加require子句,情况会变得更糟nonEmpty(它们将在案例类的构造时被调用,即在解析自身时),例如

    case class Data(location: String, mappings: List[Attribute]) {
      require(location.nonEmpty)
      require(mappings.nonEmpty)
    }
    

所以我无法正确地在解析和验证功能之间划清界限。

  1. 我想到的一种解决方案是放弃我正在使用的当前 JSON 库 ( lift-json ) 并改用play-json。它具有累积错误的功能,例如(我在这里Cats Validated了解它,与 Cats 配合得非常好)。我想我会首先将 JSON 解析为 play-json 的 JSON AST ,在我使用 play-jsons方法之间执行结构兼容验证(它会累积错误)。如果它很好地阅读案例类并执行后面的验证,我给出了上面的例子,使用 Cats。invalidNel JsValueJsValueConfigvalidateConfigJsValue
  2. 但我需要解析所有配置以查看哪个配置适用于给定日期。如果即使一个配置无法反序列化,我也不会继续。如果所有反序列化成功,我会选择(startDate, endDate)包含给定日期的那个。因此,如果我按照上面提到的解决方案,我已经将转换List[JsValue]List[Config]验证阶段。现在,如果JsValueList 中的每个都成功反序列化为一个Config实例,我可以选择适用的一个,对其执行更多验证并返回结果。但如果有些JsValue无法反序列化我该怎么办?我应该返回他们的错误吗?似乎不直观。这里的问题是我需要解析所有配置以查看哪个配置适用于给定日期。这让我更难区分解析和验证阶段。

如何在我的场景中解析和验证配置之间划清界限?我是否更改了维护版本的方式(版本从开始到结束日期都有效)?

PS:总的来说,我是一个非常新手的程序员。如果我的问题很奇怪,请原谅我。我自己从没想过在学习 Scala 的同时我会花这么多时间在验证上。

标签: scalavalidationserializationversionscala-cats

解决方案


Checks if exactly one Config in the List matches 

如果所描述的行为是要求,则格式错误的 JSON 文件是一个验证错误。您可以将 Try[List[ ]] 返回类型更改为 List[Try[ ]] 并在必要时将其与 Validated 集成。该文档可能具有使用 std lib 类的便捷方法。

如果我们可以选择第一个匹配的,那就是早午餐:进行相同的更改,然后在查找配置时在列表中找到匹配的第一个。


推荐阅读