在一组重复排序的元素中表达递归引用

时间:2016-04-16 21:56:01

标签: recursion relaxng relaxng-compact

我正在尝试编写一个具有以下规则的RelaxNG架构:

  1. line元素可以包含零个或多个ab元素。
  2. 每个a元素必须具有相应的b元素 反之亦然。
  3. a元素必须始终位于匹配的b元素之前。
  4. 因此,以下所有内容都应视为有效:

    <line><a/><b/></line>
    
    <line><a/><a/><b/><b/></line>
    
    <line><a/><a/><b/><a/><b/><b/></line>
    

    同时,以下内容均无效:

    <line><b/><a/></line>
    
    <line><a/><a/><b/></line>
    
    <line><a/></line>
    
    <line><b/></line>
    

    如何在RelaxNG中表达这一点?我的第一个想法是创建一个递归引用,如下所示:

    element line { pair* }+
    
    pair = a, pair?, b
    
    a = element a { empty }
    b = element b { empty }
    

    然而,Jing认为这是“'对'的错误递归引用”。我不能为我的生活弄清楚如何解决这个问题!有什么想法吗?

1 个答案:

答案 0 :(得分:1)

Relax NG的模式(如DTD和XSD的内容模型)基本上是正则表达式;他们在元素名称和text上定义常规语言。您似乎正在寻找的a / b匹配需要无上下文语法;因此,它不能在Relax NG中定义。

当然,它可以近似。如果你期望在实践中你永远不会有超过五个a / b对(或者更准确地说:从未超过五个a元素,你还没有看到匹配的b),你可以定义以下任何一种语言。

首先:

pair0 = (a, b)*
pair1 = (a, pair0, b)*
pair2 = (a, pair1, b)*
pair3 = (a, pair2, b)*
pair4 = (a, pair3, b)*
pair5 = (a, pair4, b)*
pair6 = (a, pair5, b)*
pair7 = (a, pair6, b)*
pair8 = (a, pair7, b)*
pair9 = (a, pair8, b)*
pair  = (a, pair9, b)*

这定义了您的语言子集,它严格执行您的两个规则,但无法处理嵌套超过10个深度的a / b对。 (或者11,取决于你的数量。)这个定义所接受的每个文件都是你真正想要的语言的成员,但并不是每个语言成员都会被接受。

如果拒绝接受该语言的有效实例,您可以按上述方式定义模式,但将pair0重新定义为:

pair0 = (a, (a|b)*, b)*

这定义了语言的超集,其中规则对a / b对强制执行,直到最大嵌套级别,但是一旦嵌套级别超过该最大值,规则就会被放弃。在这种情况下,将接受所需语言的每个成员,但不应该接受一些垃圾。

这些近似值是否适用于您的应用程序,我请您自行决定。

如果不接受近似值,您可能会发现通过不同地定义XML更容易获得所需内容。

一个简单的改变是将每个匹配的ab包装在一个元素中(我称之为e):

element line { pair* }
a = element a { empty }
b = element b { empty }
pair = element e { a, pair*, b }

现在,该文档的有效性提供了一个非常简单的保证:a元素和b元素根据需要配对,并且每个a在其匹配b之前。

鉴于ab为空,每个a紧跟e的起始标记,并且每个b紧随其后对于同一e元素的结束标记,您可以使用a的开始标记和{{1的结束标记完全替换be元素分别将e声明为

line

SAX接口,DOM接口,XSLT,XQuery以及我所知道的处理XML的所有其他方法使得将动作与元素的开头和结尾相关联就像将动作与空元素相关联一样简单。 / p>

您的有效示例将变为:

element line { e* }
e = element e { e* }

并且您的无效示例变为非正常数据。