草庐IT

xml - 使用XSD验证XML……但仍然允许扩展

coder 2024-06-23 原文

也许是我,但看来如果您有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:

A content model must be formed such that during validation [..] each item in the sequence can be uniquely determined without examining the content or attributes of that item, and without any information about the items in the remainder of the sequence.



用普通英语来说:当XML被验证并且XSD处理器遇到<SurName>时,它必须能够验证它,而无需先检查它是否后面跟着<GivenName>,即,没有期待。在您的情况下,这是不可能的。存在该规则是为了允许通过有限状态机进行实现,这应使实现变得相当简单和快速。

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

正如Marc_s所建议的那样,Relax_NG是允许使用不确定性内容模型的替代方法。但是,如果坚持使用W3C XML Schema,该怎么办?

无效的半有效解决方案

您已经注意到xs:all的限制非常严格。原因很简单:使用相同的不确定性规则,这就是为什么xs:anymin/maxOccurs大于1和序列的原因是不允许的。

另外,您可能尝试了choicesequenceany的各种组合。遇到这种无效情况时,Microsoft XSD处理器引发的错误是:

Error: Multiple definition of element 'http://example.com/Chad:SurName' causes the content model to become ambiguous. A content model must be formed such that during validation of an element information item sequence, the particle contained directly, indirectly or implicitly therein with which to attempt to validate each item in the sequence in turn can be uniquely determined without examining the content or attributes of that item, and without any information about the items in the remainder of the sequence.



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处理切换到strict处理并使类型本身更严格来实现。但是过分严格也不是一件好事,适当的平衡将取决于您判断所要面对的用例并权衡某些实现策略的权衡能力。

关于xml - 使用XSD验证XML……但仍然允许扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3347822/

有关xml - 使用XSD验证XML……但仍然允许扩展的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  10. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

随机推荐