Фон
У меня есть набор файлов конфигурации 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
с именем parseList(jsonConfigs: List[String]): Try[List[Config]]
. parseList
пытается десериализовать все конфиги в списке, каждый к экземпляру класса case Config
. Даже если один JSON не удается десериализовать, parseList
возвращает scala.util.Failure
, в противном случае возвращается scala.util.Success[List[Config]]
.
- Если
scala.util.Success[List[Config]]
возвращается из предыдущего шага, getConfig
, то, наконец, вызывает метод внутри Validator
с именем def validate(List[Config], Date): ValidationResult[Config]
и возвращает его результат. Поскольку я хочу, чтобы все ошибки накапливались, я использую Cats Validated для проверки. Я даже задал вопрос о его правильном использовании здесь .
validate
выполняет следующие действия:
Проверяет, действительно ли один Config
в Списке, применимо для данного
date (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 (они будут вызываться при создании класса case, то есть при синтаксическом анализе самого себя), например,
case class Data(location: String, mappings: List[Attribute]) {
require(location.nonEmpty)
require(mappings.nonEmpty)
}
Так что я не могу провести грань между моим анализом и моей функцией проверки правильности.
- Одним из решений, о котором я подумал, было отказаться от текущей библиотеки JSON ( lift-json ), которую я использую и вместо нее использую play-json . Он имеет функции для накопления ошибок, таких как
Cats Validated
(я узнал об этом здесь , отлично подходит для Cats invalidNel
). Я подумал, что сначала проанализирую JSON для play-json JSON AST JsValue
, выполню структурно-совместимую проверку между JsValue
и моим Config
, используя метод play-jsons validate
(он накапливает ошибки) , Если он отлично читает Config
case class из JsValue
и выполняет последние проверки, я привел примеры выше, используя Cats. - Но мне нужно проанализировать весь конфиг, чтобы увидеть, какой из них применим к определенной дате.Я не продолжаю работу, если хотя бы один конфиг не может десериализоваться.Если все десериализовано успешно, я выбираю тот, чья
(startDate, endDate)
включает данную дату.Поэтому, если я буду следовать упомянутому выше решению, я переведу List[JsValue]
в List[Config]
в фазу проверки.Теперь, если каждый JsValue
в Списке успешно десериализуется в экземпляр Config
, я могу выбрать подходящий, выполнить дополнительные проверки и вернуть результат.Но если некоторые JsValue
не смогут десериализовать, что мне делать?Должен ли я вернуть свои ошибки?Не кажется интуитивным.Эта проблема заключается в том, что мне нужно проанализировать все конфигурации, чтобы увидеть, какая из них применима для данной даты.И мне становится все труднее отмечать разделение между этапом синтаксического анализа и проверки.
Как провести черту между анализом и проверкой конфигурации в моем сценарии?Менять ли я способ поддержки версий (версия действительна с даты начала до конца)?
PS: я вообще начинающий программист.Прости меня, если мой вопрос странный.Я никогда не думал, что потрачу столько времени на проверку, изучая Scala.