你的问题有解决方案,但结果可能不太好看。原因如下:
非确定性内容模型的违规
你触及了W3C XML Schema的核心。你所要求的——变量顺序和未知元素——违反了XSD最困难、也是最基本的原则,即“非歧义性”规则,或更正式地说,
唯一粒子属性约束:
引用如下:
内容模型必须形成这样一个过程,在验证期间,可以在不检查该项的内容或属性,并且没有关于序列其余项目的任何信息的情况下,唯一确定序列中的每个项目。
通俗易懂地说:当XML被验证并且XSD处理器遇到时,它必须能够在不首先检查它是否后跟的情况下进行验证,即不能向前查看。在您的情况下,这是不可能的。此规则存在的目的是允许通过有限状态机实现,这应该使实现变得相当简单和快速。
这是最受争议的问题之一,是SGML和DTD(内容模型必须是确定性的)以及XML的遗产,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
,其命名空间为
##other
。您不能使用
##any
,除了最后一个之外,因为这将允许像
GivenName
这样的元素被用作替代,这意味着
User
的定义变得模糊不清。
第二个注意事项是,如果要使用此技巧超过两个或三个,您将不得不写下所有组合。这是一个维护噩梦。这就是我提出以下建议的原因:
一个建议的解决方案,一种变量内容容器的变体
更改您的定义。这有助于使您的读者或用户更加清晰。它还具有易于维护的优点。一系列解决方案在 XFront 上
进行了解释,这是一个不太可读的链接,您可能已经从 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表面上的这个缺陷,但正如我之前所说,如果你必须使用它,你将不得不忍受它的限制,或者接受以更高的拥有成本开发这些限制的解决方案。
备选方案
我相信你已经知道了,属性的顺序默认情况下是无法确定的。如果您所有的内容都是简单类型,您可以选择更丰富地使用属性。
最后的话
无论采取何种方法,您都将失去很多数据的可验证性。通常最好允许内容提供者添加内容类型,但仅在可以验证时才允许。这可以通过从宽松处理切换到严格处理并使类型本身更严格来实现。但过于严格也不好,正确的平衡将取决于您评估的用例和权衡某些实现策略的折衷。