使用XSD验证XML但仍允许可扩展性
也许是我,但似乎如果你有一个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:any里面的xs:sequence来允许更多的元素,不幸的是,你必须保持元素的顺序。 或者,我可以使用xs:all不强制元素的顺序,但唉,不允许我放置xs:any里面。
你的问题有一个决议,但它不会漂亮。这里是为什么:
违反非确定性内容模型 你已经谈到了W3C XML Schema的灵魂。你所要求的 – 可变顺序和可变的未知元素 – 违反XSD的最难的,最基本的原则,非歧义的规则,或者更正式地,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,不允许序列。 此外,你可能已经尝试过各种选择,序列和任何组合。 Microsoft XSD处理器在遇到此类无效情况时抛出的错误是:
在O’Reilly’s XML Schema(是的,这本书有它的缺点)这是极好的解释。幸运的是,这部分书可以在线获得。我强烈建议你阅读section 7.4.1.3 about the Unique Particle Attribution Rule,他们的解释和例子比我能得到它们更清晰。 一个工作解决方案 在大多数情况下,可以从不确定性设计转向确定性设计。这通常看起来不漂亮,但它是一个解决方案,如果你必须坚持使用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的定义变得模糊。 第二个警告是,如果你想使用这个技巧有两个或三个,你必须写下所有的组合。维修噩梦。这就是为什么我想出了以下: 建议的解决方案,变量内容容器的变体 更改您的定义。这具有为您的读者或用户更清晰的优点。它还具有变得更容易维护的优点。一整串解决方案are explained on 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切换到严格的处理,并通过使类型本身更严格。但是过于严格也不好,正确的平衡将取决于你判断你的使用案例的能力,并且权衡这些与某些实施策略的权衡。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |