如果使用scala进行版本化配置,如何将解析与验证分开?

sas*_*has 10 validation serialization scala version scala-cats

背景

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

{
  "version" : 1.0,
  "startDate": 1548419535,
  "endDate": 1558419535,
  "sourceData" : [...]  // nested json inside the List.
  "destData" : [...]    // nested json inside the List.
  "extra" : ["business_type"]
}
Run Code Online (Sandbox Code Playgroud)

有几个这样的配置文件.它们是固定的,只驻留在我的代码目录中.每个配置文件的内部表示由我的case类给出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])
Run Code Online (Sandbox Code Playgroud)

我有三个班Provider,ParserValidator.

  1. Provider有一个方法getConfig(date: Long): Config.它必须返回满足的配置startDate <= date <= endDate(理想情况下,应该存在一个这样的配置,startDateendDate定义要返回的配置版本).
  2. getConfig调用一个Parser被调用的方法parseList(jsonConfigs: List[String]): Try[List[Config]].是什么parseList做的是试图反序列化列表中的所有的configs,每到案件类的一个实例Config.即使一个JSON无法反序列化parseList返回,scala.util.Failure否则返回scala.util.Success[List[Config]].
  3. 如果scala.util.Success[List[Config]]从上一步中返回,getConfig然后最后调用一个方法里面Validatordef validate(List[Config], Date): ValidationResult[Config],并返回它的结果.因为我想要累积所有错误,我正在使用Cats验证进行验证.我甚至在这里问了一个关于它的正确用法的问题.
  4. validate执行以下操作:检查列表中是否只有一个 Config适用于给定的日期(startDate <= date <= endDate),然后对其执行一些验证Config(否则返回一个invalidNel).我执行一些基本的验证,Config比如检查各种Lists和Strings是否为空等.我还执行一些语义验证,比如检查每个字段中的每个字符串extra是否存在于mappings每个source/dest Data等等.

  1. 在最后几天困扰我的问题是,我使用的目的Cats Validated仅仅是收集所有错误(并且在遇到第一个验证错误时不会快速失败).但是当我到达validate方法时,我已经在parseList方法中做了某种验证.也就是说,我已经验证过parseList我的JSON结构与我的case类一致Config.但我parseList不会像我的validate方法那样积累错误.因此,如果我的json结构和我的case类Config之间存在许多不兼容性,我将只知道第一个.但我想立刻了解它们.
  2. 如果我开始添加require类似于nonEmptycase类内部的子句(它们将在构造case类时调用,即在解析自身时),例如,它会变得更糟

    case class Data(location: String, mappings: List[Attribute]) {
      require(location.nonEmpty)
      require(mappings.nonEmpty)
    }
    
    Run Code Online (Sandbox Code Playgroud)

因此,我无法在解析和验证功能之间划清界限.

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

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

PS:我是一名非常新手的程序员.如果我的问题很奇怪,请原谅我.我自己从未想过在学习Scala时我会花很多时间在验证上.

小智 1

Checks if exactly one Config in the List matches 
Run Code Online (Sandbox Code Playgroud)

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

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