XSD - xs:全部重写为确定性dtd

时间:2016-11-28 17:35:41

标签: xml xsd 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:类型无关]

请参阅:https://stackoverflow.com/a/7833274/6805256

链接的答案显示DTD相当于类似架构我无法根据我的需要和精确案例进行调整。

编辑:我删除了我的问题的废话部分。这个问题得到了很好的回答。

1 个答案:

答案 0 :(得分:3)

目前还不完全清楚你遇到了什么问题;要有一些具体的东西可以使用,我会假设你无法调整你引用问题的答案的原因是你不明白它是如何工作的。

您提供的内容模型允许元素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架构中都必须是 deterministic 。这意味着,必须始终可以将内容中的元素与内容模型中的特定标记(或xsd:元素元素)相匹配,而无需向前看。但是内容中的初始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的翻译很简单,我把它作为读者的练习。

[ 关于阅读此内容的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') )

但是这会插入更多的问号,而不是严格必要的;我没有找到一个好方法(或者,更诚实地,任何方式)来发出&#39;?&#39;只在必要的时候。