Проверка XML с помощью Writer и Kleisli в Scala

Это продолжение моего предыдущего вопроса

Предположим, мне нужно проверить XML следующим образом:

<a><a1>xxx<a1/><a2>yyy</a2><a3>zzz</a3></a>

Мне нужно убедиться, что корневой элемент имеет метку a а также имеет детей <a1>xxx</a1>, <a2>yyy</a2>, <a3>zzz</a3> в этом порядке.

Я хотел бы использовать List[String] собрать ошибки и определить функцию для проверки одного элемента XML, например:

type ValidateSingleElement = Elem => List[String]

Теперь я могу написать функции для проверки метки, текста и атрибутов данного элемента XML:

val label : String => ValidateSingleElement = ...
val text  : String => ValidateSingleElement = ...
val attr  : (String, String) => ValidateSingleElement = ...

Я могу составить эти функции с |+| поскольку ValidateSingleElement это моноид

val a1 = label("a1") |+| text("xxx") // validate both label and text

Теперь мне нужна функция для проверки дочерних элементов данного элемента. Чтобы написать такую ​​функцию, мне нужна еще одна функция для проверки последовательности элементов.

val children: ValidateElements => ValidateSingleElement = ...

ValidateElements определяется следующим образом:

type ValidateElements = List[Elem] => Writer[List[String], List[Elem]]

Я использую List[String] а также Writer Монаду собирать ошибки при обходе последовательности элементов.

Теперь я могу написать функцию для проверки дочерних элементов данного элемента:

val children: ValidateElements => ValidateSingleElement = 
  validateElements => {e =>
    val kids = e.child collect {case e:Elem => e}
    val writer = validateElements(kids.toList)
    writer.written
  }

... и проверить первый элемент последовательности:

 val child: ValidateSingleElement => ValidateElements = validate => {
   _ match {
     case e:es => Writer(validate(e), es)
     case _    => Writer(List("Unexpected end of input"), Nil)
   }
 }

Наконец я могу переопределить ValidateElements как Kleisli

type ErrorsWriter[A]  = Writer[List[String], A]
type ValidateElements = Kliesli[ErrorsWriter, List[Elem], List[Elem]]

... и переписать child вернуть Kleisli вместо функции.

Учитывая как child а также children я могу написать a - проверяющая функция для XML сверху:

val a1 = label("a1") |+| text("xxx")
val a2 = label("a2") |+| text("yyy")
val a3 = label("a3") |+| text("zzz")
val a  = label("a")  |+| children(child(a1) >=> child(a2) >=> child(a3))

Имеет ли это смысл? Как бы вы исправили / расширили этот дизайн?

1 ответ

Ну, в большинстве случаев вы не хотите проверять только XML-документ, вы хотите создать из него какой-то значимый бизнес-объект, и ваш код, похоже, этого не допускает. Я думаю, что библиотека Json, основанная на классах типов, - хорошая модель того, как это сделать. Это позволяет вам определить Reads объекты, где Reads[A] по сути JsValue => Either[Errors, A], Их можно гибко комбинировать с кучей комбинаторов, поставляемых с библиотекой.

Другие вопросы по тегам