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

Express a recursive reference in a set of recurring ordered elements

我正在尝试编写具有以下规则的 RelaxNG 模式:

  1. 一个 line 元素可以包含零个或多个 ab 元素。
  2. 每个 a 元素必须有一个对应的 b 元素并且 反之亦然。
  3. a 元素必须始终在其匹配的 b 元素之前。

所以,以下都应该被认为是有效的:

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

不过,璟认为这是"bad recursive reference to 'pair'"。我一辈子都想不出如何解决这个问题!有什么想法吗?

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

这定义了你的语言的子集,它严格执行你的两个规则,但不能处理 a/b 嵌套超过 10 层的对。 (或 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 元素的结束标记之前,您可以将 ab 元素完全替换为 e 的开始标记和 e,分别声明 line

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

SAX 接口、DOM 接口、XSLT、XQuery 以及我所知道的所有其他处理方法 XML 使得将操作与元素的开始和结束相关联大致一样简单是将动作与空元素相关联。

您的有效示例将变为:

<line><e></e></line>
<line><e><e></e></e></line>
<line><e><e></e><e></e></e></line>

并且您的无效示例变成格式不正确的数据。