XSD - xs:all 重写为确定性 dtd

XSD - xs:all rewritten as deterministic dtd

给出以下 xsd 部分:

<xs:element name="root">
    <xs:complexType>
        <xs:all>
            <xs:element minOccurs="1" name="box-type-1"/>
            <xs:element minOccurs="0" name="box-type-2"/>
            <xs:element minOccurs="0" name="box-type-3"/>
            <xs:element minOccurs="0" name="box-type-4"/>
        </xs:all>
    </xs:complexType>
</xs:element>

[xs:类型无关]

请看:

链接的答案显示了类似模式的 DTD 等价物但是我无法根据我的需要和具体情况调整它。

编辑:我删除了问题中无意义的部分。问题完美解答

您遇到的问题还不是很清楚;有一些具体的工作,我会假设你不能调整你引用的答案来解决你的问题的原因是你不明白它是如何工作的。

您提供的内容模型允许元素 box-type-1box-type-4 以任意顺序出现;因为有四个元素,所以有 4 个! = 4*3*2*1 = 24 个可能的序列。类似 DTD 语法的简单正则表达式看起来像这样;为简洁起见,我将调用元素 b1b2b3b4.

( (b1, b2, b3, b4)
| (b1, b2, b4, b3)
| (b1, b3, b2, b4)
| (b1, b3, b4, b2)
| (b1, b4, b2, b3) 
| (b1, b4, b2, b3)

| (b2, b1, b3, b4) 
| (b2, b1, b4, b3) 
| (b2, b3, b1, b4) 
| (b2, b3, b4, b1) 
| (b2, b4, b1, b3) 
| (b2, b4, b3, b1) 

| (b3, b1, b2, b4) 
| (b3, b1, b4, b2) 
| (b3, b2, b1, b4) 
| (b3, b2, b4, b1) 
| (b3, b4, b1, b2) 
| (b3, b4, b2, b1) 

| (b4, b1, b2, b3) 
| (b4, b1, b3, b2) 
| (b4, b2, b1, b3) 
| (b4, b2, b3, b1) 
| (b4, b3, b1, b2) 
| (b4, b3, b2, b1) 
) 

到目前为止一切顺利,但有两个问题:首先,内容模型在 XML DTD 和 XSD 中都需要 确定性 模式。这意味着,必须始终可以将内容中的元素与内容模型中的特定标记(或 xsd:element 元素)匹配,而无需向前看。但是内容中的初始 b1 可以匹配内容模型中前六次出现的 b1 中的任何一个。这是不允许的,所以我们需要重写内容模型来消除非确定性。

所以我们重写表达式,利用观察序列的代数恒等式,((x y abc) | (x z def)) 等于 (x ((y abc) | (z def)))。如果我们按照该身份的建议将共同的前缀折叠在一起,那么我们就会产生一个确定性的表达式,它可以识别完全相同的语言:

( (b1, ( (b2, ( (b3, b4) | (b4, b3) )) 
       | (b3, ( (b2, b4) | (b4, b2) ))
       | (b4, ( (b2, b3) | (b3, b2) )) ))
| (b2, ( (b1, ( (b3, b4) | (b4, b3) )) 
       | (b3, ( (b1, b4) | (b4, b1) ))
       | (b4, ( (b1, b3) | (b3, b1) )) )) 
| (b3, ( (b1, ( (b2, b4) | (b4, b2) )) 
       | (b2, ( (b1, b4) | (b4, b1) ))
       | (b4, ( (b1, b2) | (b2, b1) )) )) 
| (b4, ( (b1, ( (b2, b3) | (b3, b2) ))
       | (b2, ( (b1, b3) | (b3, b1) )) 
       | (b3, ( (b1, b2) | (b2, b1) )) )) )

现在我们遇到了第二个复杂问题:这不是我们想要的语言,因为它使所有四个元素都是必需的。

使 b2-b4 可选的一种简单方法是在表达式中为它们中的每一个添加一个问号,因此它的形式如下所示:

( (b1, ( (b2?, ( (b3?, b4?) | (b4?, b3?) )) 
       | (b3?, ( (b2?, b4?) | (b4?, b2?) ))
       | (b4?, ( (b2?, b3?) | (b3?, b2?) )) ))
| (b2?, ... )
| (b3?, ... )
| (b4?, ... ) )

但这重新引入了非确定性:输入序列 b1b2 仅在显示的表达式部分匹配五种方式。正确的解决方法是观察 while ((x?, y?) | (y? , x?)) 是不确定的,表达式 ((x, y?) | (y, x?))?是确定性的并接受相同的语言。应用这个原则可以让我们适当地重写我们的表达式:

( (b1, ( (b2, ( (b3, b4?) | (b4, b3?) )?) 
       | (b3, ( (b2, b4?) | (b4, b2?) )?)
       | (b4, ( (b2, b3?) | (b3, b2?) )?) )?)
| (b2, ( (b1, ( (b3, b4?) | (b4, b3?) )?) 
       | (b3, ( (b1, b4?) | (b4, b1) ))
       | (b4, ( (b1, b3?) | (b3, b1) )) )) 
| (b3, ( (b1, ( (b2, b4?) | (b4, b2?) )?) 
       | (b2, ( (b1, b4?) | (b4, b1) ))
       | (b4, ( (b1, b2?) | (b2, b1) )) )) 
| (b4, ( (b1, ( (b2, b3?) | (b3, b2?) )?)
       | (b2, ( (b1, b3?) | (b3, b1) )) 
       | (b3, ( (b1, b2?) | (b2, b1) )) )) )

请注意,我们不会将第一个 b1 左侧的任何标记标记为可选,因为那样做是不必要和不可取的。不必要的,因为我们总是可以不失一般性地假设在上面给出的排列之一的末尾省略了未出现在正在验证的输入中的可选元素。不可取,因为它还会重新引入非确定性。

从这个表达式到 XSD 的翻译很简单,我把它留作 reader 的练习。

[阅读本文的 XML 极客请注意: 其他人现在可能会停止阅读。]

我编写的用于生成不带问号的左折叠表达式的 XQuery 函数可能很有趣:

declare function local:all-to-choice-of-seq(
  $ids as xs:string*
) as xs:string { 
  let $n := count($ids)
  return if ($n eq 1) then $ids
  else '( ' || 
       string-join( for $id in $ids
       let $rest := $ids[. ne $id]
       return '(' || $id || ', ' 
              || local:all-to-choice-of-seq($rest) || ')',
       ' | ')
       || ' )'
};

调用:

let $gis := ('b1', 'b2', 'b3', 'b4')
return local:all-to-choice-of-seq( $gis ) 

使一些元素成为必需元素而另一些元素成为可选元素的简单扩展是:

declare function local:all-to-choice-of-seq2(
  $req as xs:string*,
  $opt as xs:string*
) as xs:string { 
  local:all-to-choice-of-seq-aux(($req,$opt), $req)
};

declare function local:all-to-choice-of-seq-aux(
  $ids as xs:string*,
  $req as xs:string* 
) as xs:string { 
  let $n := count($ids)
  return 
  if ($n eq 1) then 
     if (exists($req)) then $ids else $ids || '?'
  else '( ' || 
       string-join( for $id in $ids
       let $rest := $ids[. ne $id],
           $req2 := $req[. ne $id]
       return '(' || $id || ', ' 
              || local:all-to-choice-of-seq-aux($rest, $req2) || ')'
              || (if (exists($req)) then '' else '?'),
       ' | ')
       || ' )'
};

调用:local:all-to-choice-of-seq2( 'b1', ('b2', 'b3', 'b4') )

但这插入了比绝对必要的更多的问号;我还没有找到发出 '?' 的好方法(或者,更诚实地说,any 方法)仅在必要时。