34

也许是我,但如果你有 XSD

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="User">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="GivenName" />
                <xs:element name="SurName" />
            </xs:sequence>
            <xs:attribute name="ID" type="xs:unsignedByte" use="required" />
        </xs:complexType>
    </xs:element>
</xs:schema>

定义此文档的架构

<?xml version="1.0" encoding="utf-8" ?>
<User ID="1">
    <GivenName></GivenName>
    <SurName></SurName>
</User>

如果您添加了另一个元素(例如 EmailAddress)并混淆了顺序,它将无法验证

<?xml version="1.0" encoding="utf-8" ?>
<User ID="1">
    <SurName></SurName>
    <EmailAddress></EmailAddress>
    <GivenName></GivenName>
</User>

我不想将 EmailAddress 添加到文档中并将其标记为可选。

我只想要一个 XSD 来验证文档必须满足的最低要求。

有没有办法做到这一点?

编辑:

marc_s 在下面指出,您可以使用xs:anyinside ofxs:sequence来允许更多元素,不幸的是,您必须保持元素的顺序。

或者,我可以使用xs:allwhich 不强制元素的顺序,但唉,不允许我放置xs:any在其中。

4

5 回答 5

57

你的问题有一个解决方案,但它不会很漂亮。原因如下:

违反非确定性内容模型

您已经触及了 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:anywith##other作为它的命名空间。除了最后一个,您不能使用##any,因为这将允许GivenName使用喜欢的元素代替,这意味着 的定义User变得模棱两可。

第二个警告是,如果你想在两个或三个以上的情况下使用这个技巧,你必须写下所有的组合。维护的噩梦。这就是为什么我想出以下内容:

建议的解决方案,可变内容容器的变体

改变你的定义。这样做的好处是让您的读者或用户更清楚。它还具有易于维护的优点。在 XFront 上解释了一整串解决方案here ,您可能已经从 Oleg 的帖子中看到了一个可读性较差的链接。这是一本很好的读物,但大部分内容都没有考虑到变量内容容器内至少有两个元素的要求。

当前针对您的情况的最佳实践方法(这种情况比您想象的更频繁)是将您的数据拆分为必填字段和非必填字段。您可以添加一个元素<Required>,或者相反,添加一个元素<ExtendedInfo>(或称其为PropertiesOptionalData)。这看起来如下:

<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 明显缺陷的人,但正如我之前所说,如果你必须使用它,你将不得不忍受它的局限性,或者接受开发的负担以更高的拥有成本绕过这些限制。

替代解决方案

我确定您已经知道这一点,但属性的顺序默认情况下是未确定的。如果你所有的内容都是简单类型的,你也可以选择更丰富地使用属性。

最后一句话

无论您采取何种方法,您都将失去大量数据的可验证性。允许内容提供者添加内容类型通常更好,但前提是可以验证。您可以通过从处理切换laxstrict处理并使类型本身更严格来做到这一点。但是过于严格也不好,正确的平衡将取决于你判断你所面对的用例的能力,并权衡某些实施策略的权衡。

于 2010-08-05T00:38:26.823 回答
6

在阅读了marc_s的答案和您在评论中的讨论后,我决定补充一点。

在我看来,您的问题Chad没有完美的解决方案。有一些方法可以在 XSD 中实现可扩展内容模型,但我所知的所有实现都有一些限制。因为您没有写关于您计划使用可扩展 XSD 的环境,所以您只能推荐一些链接,这些链接可能会帮助您选择可以在您的环境中实现的方式:

  1. http://www.xfront.com/ExtensibleContentModels.html(或http://www.xfront.com/ExtensibleContentModels.pdf)和http://www.xfront.com/VariableContentContainers.html
  2. http://www.xml.com/lpt/a/993(或http://www.xml.com/pub/a/2002/07/03/schema_design.html
  3. http://msdn.microsoft.com/en-us/library/ms950793.aspx
于 2010-08-01T20:33:00.463 回答
4

您应该能够使用<xs:any>元素扩展您的架构以实现可扩展性 - 有关详细信息,请参阅W3Schools

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="User">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="GivenName" />
                <xs:element name="SurName" />
                <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
            </xs:sequence>
            <xs:attribute name="ID" type="xs:unsignedByte" use="required" />
        </xs:complexType>
    </xs:element>
</xs:schema>

当您添加时processContents="lax",.NET XML 验证应该会成功。

有关更多详细信息,请参阅xs:any 上的 MSDN 文档。

更新:如果您需要更大的灵活性和不那么严格的验证,您可能希望查看为 XML 定义模式的其他方法 - 例如RelaxNG。XML Schema - 故意 - 对它的规则相当严格,所以也许这只是手头工作的错误工具。

于 2010-07-27T20:55:27.220 回答
1

好吧,您总是可以使用 DTD :-) 除了 DTD 还规定了排序。使用“无序”语法进行验证非常昂贵。您可以使用 xsd:choice 和 min 和 max 出现,但它可能也会犹豫。您还可以编写 XSD 扩展/派生模式。

您提出问题的方式看起来您根本不想要 XSD。您可以加载它,然后使用 XPaths 验证您想要的任何最小值,但只是抗议 XSD,在它成为无所不在的标准多少年后,真的,真的不会让你到任何地方。

于 2010-08-04T14:15:08.493 回答
1

如果您可以使用,RelaxNG 将简洁地解决这个问题。确定性不是模式的要求。您可以将 RNG 或 RNC 模式转换为 XSD,但在这种情况下它会近似。这是否足以供您使用取决于您。

这种情况下的 RNC 模式是:

start = User
User = element User {
   attribute ID { xsd:unsignedByte },
   ( element GivenName { text } &
     element SurName { text } &
     element * - (SurName | GivenName) { any })
}

any = element * { (attribute * { text } | text | any)* }

any 规则匹配任何格式良好的 XML 片段。因此,这将要求 User 元素包含 GivenName 和 SurName 元素,其中包含任何顺序的文本,并允许任何其他元素包含几乎任何内容。

于 2013-08-12T19:02:11.380 回答