代码之家  ›  专栏  ›  技术社区  ›  CaffGeek

正在用XSD验证XML…但仍允许扩展性

  •  31
  • CaffGeek  · 技术社区  · 14 年前

    也许是我,但如果你有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>
    

    我不想在文档中添加电子邮件地址并将其标记为可选。

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

    有办法吗?

    编辑:

    Marc_在下面指出你可以使用 xs:any 里面 xs:sequence 不幸的是,为了允许更多的元素,您必须保持元素的顺序。

    或者,我可以使用 xs:all 它不执行元素的顺序,但遗憾的是,它不允许我放置 任何一个 里面。

    5 回复  |  直到 6 年前
        1
  •  53
  •   CaffGeek    6 年前

    你的问题已经解决了,但不会很漂亮。这就是为什么:

    违反非确定性内容模型

    您已经触及了W3C XML模式的精髓。 变量未知元素-违反了XSD最困难但最基本的原则,即 非歧义 或者更正式地说, Unique Particle Attribution Constraint :

    必须形成这样的内容模型 在验证期间,每个项目 在序列中可以唯一 在不检查 该项目的内容或属性, 没有任何关于 剩余项中的项 序列。

    在普通英语中:当XML被验证并且XSD处理器遇到 <SurName> 它必须能够在不首先检查后面是否有 <GivenName> 也就是说,没有前瞻性。在您的场景中,这是不可能的。这个规则允许通过有限状态机实现,这将使实现变得非常简单和快速。

    这是最有争议的问题之一,是SGML和DTD(内容模型)的传统 必须是确定性的 )和XML,默认情况下,它定义元素的顺序 是重要的 (因此,尝试相反的方法,使顺序不重要,是很困难的)。

    正如Marc_已经建议的那样,RELAX_ng是一个允许非确定性内容模型的替代方案。但是,如果您坚持使用W3CXML模式,您可以做什么呢?

    非工作半有效解

    你已经注意到了 xs:all 是非常严格的。原因很简单:同样的非确定性规则也适用,这就是为什么 xs:any , min/maxOccurs 不允许大于1和序列。

    另外,你可能尝试过各种组合 choice , sequence any . 遇到这种无效情况时,Microsoft XSD处理器抛出的错误是:

    错误:元素的多个定义 ’ http://example.com/Chad:SurName ’ 使内容模型成为 模棱两可的。内容模型必须 在验证期间 元素信息项序列, 直接包含的粒子, 间接地或隐含地 尝试验证每个项目 依次顺序可以是 唯一确定,无需检查 它的内容或属性 项目,没有任何信息 关于剩余的项 序列。

    O'Reilly's XML Schema (是的,这本书有缺点)这解释得很好。此外,这本书的部分内容可在网上查阅。我强烈建议你通读 section 7.4.1.3 about the Unique Particle Attribution Rule 他们的解释和例子比我所能得到的要清楚得多。

    一个有效的解决方案

    在大多数情况下,从不确定性设计到确定性设计是可能的。这通常看起来不漂亮,但如果必须坚持W3CXML模式和/或必须允许对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>
    

    上面的代码实际上 只是作品 . 但有一些警告。第一个是 任何一个 具有 ##other 作为其命名空间。不能使用 ##any ,除了最后一个元素,因为这将允许 GivenName 用在那个地方,这意味着 User 变得模棱两可。

    第二点需要注意的是,如果你想在两个或三个以上的地方使用这个技巧,你必须写下所有的组合。维护的噩梦。这就是我提出以下问题的原因:

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

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

    当前针对您的情况的最佳实践方法(比您可能想象的更频繁)是将数据拆分为必需和非必需字段。可以添加元素 <Required> 或者相反,添加一个元素 <ExtendedInfo> (或称之为 性质 选择数据 )如下所示:

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

    替代方案

    我敢肯定你已经知道了,但是属性的顺序在默认情况下是不确定的。如果您的所有内容都是简单的类型,那么您可以选择更充分地使用属性。

    最后一句话

    无论采用什么方法,都会失去很多数据的可验证性。通常最好允许内容提供者添加内容类型,但只有在可以验证时才允许。您可以通过从 lax strict 处理和使类型本身更严格。但是过于严格也不好,正确的平衡将取决于您判断所面临的用例的能力,以及将其与某些实现策略的权衡进行权衡。

        2
  •  6
  •   Oleg    14 年前

    读完答案后 马尔库斯 你在评论中的讨论,我决定加一点。

    在我看来 没有完美的解决方案 关于你的问题 乍得 . 在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
        3
  •  4
  •   marc_s HarisH Sharma    14 年前

    您应该能够用 <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验证应该会成功。

    MSDN docs on xs:any 了解更多详细信息。

    更新: 如果您需要更大的灵活性和不太严格的验证,那么您可能希望了解为XML定义模式的其他方法,比如 RelaxNG . XML模式是有目的的,对其规则相当严格,所以这可能是目前这项工作的错误工具。

        4
  •  1
  •   ZXX    14 年前

    好吧,除了DTD还规定了订购之外,您可以一直使用DTD:-。使用“无序”语法进行验证非常昂贵。你可以玩xsd:choice,最小值和最大值也会出现,但它可能也会停止。您还可以编写XSD扩展/派生模式。

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

        5
  •  1
  •   Mike William Meyer    11 年前

    如果你能使用它,放松会简单地解决这个问题。确定性不是模式的要求。您可以将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片段。因此,这将要求用户元素以任何顺序包含givenname和姓氏元素,并允许任何其他元素包含几乎所有内容。