Как отделить разбор от проверки в случае версионного конфига с использованием scala? - PullRequest
0 голосов
/ 25 января 2019

Фон

У меня есть набор файлов конфигурации 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.

  1. Provider имеет метод getConfig(date: Long): Config. Он должен возвращать конфигурацию, удовлетворяющую startDate <= date <= endDate (в идеале должен присутствовать ровно один такой конфигурационный файл, поскольку startDate - endDate определяет возвращаемую версию конфигурации).
  2. getConfig вызывает метод внутри Parser с именем parseList(jsonConfigs: List[String]): Try[List[Config]]. parseList пытается десериализовать все конфиги в списке, каждый к экземпляру класса case Config. Даже если один JSON не удается десериализовать, parseList возвращает scala.util.Failure, в противном случае возвращается scala.util.Success[List[Config]].
  3. Если scala.util.Success[List[Config]] возвращается из предыдущего шага, getConfig, то, наконец, вызывает метод внутри Validator с именем def validate(List[Config], Date): ValidationResult[Config] и возвращает его результат. Поскольку я хочу, чтобы все ошибки накапливались, я использую Cats Validated для проверки. Я даже задал вопрос о его правильном использовании здесь .
  4. validate выполняет следующие действия: Проверяет, действительно ли один Config в Списке, применимо для данного date (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 (они будут вызываться при создании класса case, то есть при синтаксическом анализе самого себя), например,

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

Так что я не могу провести грань между моим анализом и моей функцией проверки правильности.

  1. Одним из решений, о котором я подумал, было отказаться от текущей библиотеки 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.
  2. Но мне нужно проанализировать весь конфиг, чтобы увидеть, какой из них применим к определенной дате.Я не продолжаю работу, если хотя бы один конфиг не может десериализоваться.Если все десериализовано успешно, я выбираю тот, чья (startDate, endDate) включает данную дату.Поэтому, если я буду следовать упомянутому выше решению, я переведу List[JsValue] в List[Config] в фазу проверки.Теперь, если каждый JsValue в Списке успешно десериализуется в экземпляр Config, я могу выбрать подходящий, выполнить дополнительные проверки и вернуть результат.Но если некоторые JsValue не смогут десериализовать, что мне делать?Должен ли я вернуть свои ошибки?Не кажется интуитивным.Эта проблема заключается в том, что мне нужно проанализировать все конфигурации, чтобы увидеть, какая из них применима для данной даты.И мне становится все труднее отмечать разделение между этапом синтаксического анализа и проверки.

Как провести черту между анализом и проверкой конфигурации в моем сценарии?Менять ли я способ поддержки версий (версия действительна с даты начала до конца)?

PS: я вообще начинающий программист.Прости меня, если мой вопрос странный.Я никогда не думал, что потрачу столько времени на проверку, изучая Scala.

1 Ответ

0 голосов
/ 31 января 2019
Checks if exactly one Config in the List matches 

Если описанное поведение является требованием, искаженные файлы JSON являются ошибкой проверки. Вы можете изменить тип возвращаемого значения Try [List []] на List [Try []] и при необходимости интегрировать его с Validated. В документации, вероятно, есть удобные методы для работы с классами std lib.

Если мы можем взять первый, который соответствует, это ранний обед: внесите то же самое изменение и просто найдите первый в списке, который соответствует при поиске конфигурации.

...