你的问题有一个解决方案,但它不会很漂亮。原因如下:
违反非确定性内容模型
您已经触及了 W3C XML Schema 的精髓。你所问的——可变顺序和可变未知元素——违反了 XSD 中最难但最基本的原则,即Non-Ambiguity规则,或者更正式地说,Unique Particle Attribution Constraint:
必须形成一个内容模型,以便在验证期间 [..] 可以唯一确定序列中的每个项目,而无需检查该项目的内容或属性,也无需任何有关序列其余项目的信息。
用普通的英语:当一个XML被验证并且XSD处理器遇到<SurName>
它时,它必须能够验证它,而无需先检查它是否跟在后面<GivenName>
,即不向前看。在您的情况下,这是不可能的。该规则的存在是为了允许通过有限状态机实现,这应该使实现变得相当简单和快速。
这是最受争议的问题之一,是 SGML 和 DTD(内容模型必须是确定性的)和 XML 的遗产,默认情况下,它定义元素的顺序很重要(因此,尝试相反,制定顺序不重要,很难)。
正如 Marc_s 已经建议的那样,Relax_NG 是允许非确定性内容模型的替代方案。但是,如果您被 W3C XML Schema 卡住了怎么办?
无效的半有效解决方案
您已经注意到这xs:all
是非常严格的。原因很简单:同样的非确定性规则适用,这就是为什么xs:any
,min/maxOccurs
大于 1 和序列是不允许的。
choice
此外,您可能已经尝试过,sequence
和的各种组合any
。Microsoft XSD 处理器遇到这种无效情况时抛出的错误是:
错误:元素“ http://example.com/Chad:SurName ”的多重定义导致内容模型变得不明确。必须形成一个内容模型,使得在元素信息项序列的验证过程中,直接、间接或隐含地包含在其中的粒子,用于依次验证序列中的每个项目,可以唯一确定,而无需检查其内容或属性。该项目,并且没有关于序列其余部分中的项目的任何信息。
在O'Reilly 的 XML Schema中(是的,这本书有其缺陷)对此进行了很好的解释。幸运的是,本书的部分内容可以在线获取。我强烈建议您通读第 7.4.1.3 节关于独特粒子归属规则的内容,他们的解释和示例比我能得到的要清楚得多。
一种可行的解决方案
在大多数情况下,可以从不确定的设计转变为确定的设计。这通常看起来不太漂亮,但如果您必须坚持使用 W3C XML Schema 和/或如果您绝对必须允许对您的 XML 使用非严格规则,那么它是一个解决方案。您的情况的噩梦是您想要强制执行一件事(2 个预定义元素),同时又想要让它非常松散(顺序无关紧要,任何事情都可以在之前和之后进行)。如果我不尝试给您好的建议而只是直接带您找到解决方案,它将如下所示:
<xs:element name="User">
<xs:complexType>
<xs:sequence>
<xs:any minOccurs="0" processContents="lax" namespace="##other" />
<xs:choice>
<xs:sequence>
<xs:element name="GivenName" />
<xs:any minOccurs="0" processContents="lax" namespace="##other" />
<xs:element name="SurName" />
</xs:sequence>
<xs:sequence>
<xs:element name="SurName" />
<xs:any minOccurs="0" processContents="lax" namespace="##other" />
<xs:element name="GivenName" />
</xs:sequence>
</xs:choice>
<xs:any minOccurs="0" processContents="lax" namespace="##any" />
</xs:sequence>
<xs:attribute name="ID" type="xs:unsignedByte" use="required" />
</xs:complexType>
</xs:element>
上面的代码实际上只是工作。但有一些警告。第一个是xs:any
with##other
作为它的命名空间。除了最后一个,您不能使用##any
,因为这将允许GivenName
使用喜欢的元素代替,这意味着 的定义User
变得模棱两可。
第二个警告是,如果你想在两个或三个以上的情况下使用这个技巧,你必须写下所有的组合。维护的噩梦。这就是为什么我想出以下内容:
建议的解决方案,可变内容容器的变体
改变你的定义。这样做的好处是让您的读者或用户更清楚。它还具有易于维护的优点。在 XFront 上解释了一整串解决方案here ,您可能已经从 Oleg 的帖子中看到了一个可读性较差的链接。这是一本很好的读物,但大部分内容都没有考虑到变量内容容器内至少有两个元素的要求。
当前针对您的情况的最佳实践方法(这种情况比您想象的更频繁)是将您的数据拆分为必填字段和非必填字段。您可以添加一个元素<Required>
,或者相反,添加一个元素<ExtendedInfo>
(或称其为Properties或OptionalData)。这看起来如下:
<xs:element name="User2">
<xs:complexType>
<xs:sequence>
<xs:element name="GivenName" />
<xs:element name="SurName" />
<xs:element name="ExtendedInfo" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax" namespace="##any" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
目前这似乎不太理想,但让它成长一点。拥有一组有序的固定元素并不是什么大不了的事。你不是唯一一个抱怨 W3C XML Schema 明显缺陷的人,但正如我之前所说,如果你必须使用它,你将不得不忍受它的局限性,或者接受开发的负担以更高的拥有成本绕过这些限制。
替代解决方案
我确定您已经知道这一点,但属性的顺序默认情况下是未确定的。如果你所有的内容都是简单类型的,你也可以选择更丰富地使用属性。
最后一句话
无论您采取何种方法,您都将失去大量数据的可验证性。允许内容提供者添加内容类型通常更好,但前提是可以验证。您可以通过从处理切换lax
到strict
处理并使类型本身更严格来做到这一点。但是过于严格也不好,正确的平衡将取决于你判断你所面对的用例的能力,并权衡某些实施策略的权衡。