scala - 在使用 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])
我有三个班Provider
,Parser
和Validator
。
Provider
有方法getConfig(date: Long): Config
。它必须返回令人满意的配置startDate <= date <= endDate
(理想情况下,应该存在一个这样的配置,startDate
以endDate
定义要返回的配置版本)。getConfig
调用内部的一个方法Parser
calledparseList(jsonConfigs: List[String]): Try[List[Config]]
。什么parseList
是尝试反序列化列表中的所有配置,每个配置为 case class 的一个实例Config
。即使一个 JSON 无法反序列化,它也会parseList
返回 a ,scala.util.Failure
否则它会返回scala.util.Success[List[Config]]
。- 如果
scala.util.Success[List[Config]]
是上一步返回的,那么最后会调用getConfig
被调用的方法,并返回它的结果。由于我希望累积所有错误,因此我使用Cats Validated进行验证。我什至在这里问了一个关于它的正确用法的问题。Validator
def validate(List[Config], Date): ValidationResult[Config]
validate
执行以下操作:检查列表中是否恰好有一个Config
适用于给定日期(startDate <= date <= endDate
),然后对其执行一些验证Config
(否则它返回一个invalidNel
)。我执行一些基本验证,Config
例如检查各种列表和字符串是否为非空等。我还执行一些语义验证,例如检查字段中的每个字符串extra
是否存在于mappings
每个source/dest Data
等中。
问题
- 最近几天困扰我的问题是,我使用的目的
Cats Validated
仅仅是收集所有错误(而不是在遇到第一个验证错误时快速失败)。但是当我到达validate
方法时,我已经在parseList
方法中进行了某种验证。也就是说,我已经验证了parseList
我的 JSON 结构符合我的案例类Config
。但我parseList
不会像我的validate
方法那样积累错误。因此,如果我的 json 结构和我的案例类Config
之间存在许多不兼容性,我只会了解第一个。但我想同时了解它们。 如果我开始只在案例类中添加
require
子句,情况会变得更糟nonEmpty
(它们将在案例类的构造时被调用,即在解析自身时),例如case class Data(location: String, mappings: List[Attribute]) { require(location.nonEmpty) require(mappings.nonEmpty) }
所以我无法正确地在解析和验证功能之间划清界限。
- 我想到的一种解决方案是放弃我正在使用的当前 JSON 库 ( lift-json ) 并改用play-json。它具有累积错误的功能,例如(我在这里
Cats Validated
了解它,与 Cats 配合得非常好)。我想我会首先将 JSON 解析为 play-json 的 JSON AST ,在我使用 play-jsons方法之间执行结构兼容验证(它会累积错误)。如果它很好地阅读案例类并执行后面的验证,我给出了上面的例子,使用 Cats。invalidNel
JsValue
JsValue
Config
validate
Config
JsValue
- 但我需要解析所有配置以查看哪个配置适用于给定日期。如果即使一个配置无法反序列化,我也不会继续。如果所有反序列化成功,我会选择
(startDate, endDate)
包含给定日期的那个。因此,如果我按照上面提到的解决方案,我已经将转换List[JsValue]
为List[Config]
验证阶段。现在,如果JsValue
List 中的每个都成功反序列化为一个Config
实例,我可以选择适用的一个,对其执行更多验证并返回结果。但如果有些JsValue
无法反序列化我该怎么办?我应该返回他们的错误吗?似乎不直观。这里的问题是我需要解析所有配置以查看哪个配置适用于给定日期。这让我更难区分解析和验证阶段。
如何在我的场景中解析和验证配置之间划清界限?我是否更改了维护版本的方式(版本从开始到结束日期都有效)?
PS:总的来说,我是一个非常新手的程序员。如果我的问题很奇怪,请原谅我。我自己从没想过在学习 Scala 的同时我会花这么多时间在验证上。
解决方案
Checks if exactly one Config in the List matches
如果所描述的行为是要求,则格式错误的 JSON 文件是一个验证错误。您可以将 Try[List[ ]] 返回类型更改为 List[Try[ ]] 并在必要时将其与 Validated 集成。该文档可能具有使用 std lib 类的便捷方法。
如果我们可以选择第一个匹配的,那就是早午餐:进行相同的更改,然后在查找配置时在列表中找到匹配的第一个。
推荐阅读
- android - 使用网络摄像头在模拟器(android studio)上运行 ARCore 项目
- python - 在python中调用r脚本?
- javascript - 在 angular/typescript 中触发 bootstrap js collpase() 函数
- vue.js - 不工作...mapGetters
- wordpress - Wordpress 三级菜单自定义 Walker
- angular - 离子形式组补丁值补丁作为字符串数组中的字符串
- angular - 在 Angular2 打字稿类中找不到模块
- javascript - 如何使用 Jest 模拟原生 javascript 方法?
- c++ - C++ 递归函数中的分段错误
- python - Tensorflow 根据其元素和元素的索引创建矩阵