1

这是我之前的问题的后续

假设我需要验证这样的 XML:

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

我需要确保根元素有 labela并且也有 children <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]and Writermonad 来收集错误。

现在我可以编写一个函数来验证给定元素的子元素:

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)
   }
 }

最后我可以重新定义ValidateElementsKleisli

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

...并重新编写child返回Kleisli而不是函数。

鉴于两者childchildren我可以编写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))

是否有意义 ?您将如何纠正/扩展此设计?

4

1 回答 1

1

好吧,在大多数情况下,您不想只验证 XML 文档,而是想从中创建一些有意义的业务对象,而您的代码似乎不允许这样做。我认为 Play 的基于类型类的 Json 库是如何做到这一点的一个很好的模型。它允许您定义Reads对象,其中 aReads[A]本质上是 a JsValue => Either[Errors, A]。这些可以与库附带的一堆组合器灵活组合。

于 2016-01-31T01:37:30.957 回答