指定xsi:type允许一个将根元素的名称更改为任何值?

时间:2016-04-27 09:53:59

标签: xml validation xsd xerces xmllint

我正在尝试使用xsi:type,并注意到当根元素中存在xsi:type属性时,根元素的名称似乎在XSD验证中不起任何作用。

SSCCE紧随其后。

A.xsd是:

<xs:schema targetNamespace="foo://a" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           xmlns="foo://a">

   <xs:element name="type" type="Type"/>

   <xs:simpleType name="Type">
     <xs:restriction base="xs:token">
         <xs:enumeration value="Archive"/>
         <xs:enumeration value="Organisation"/>
       </xs:restriction>
   </xs:simpleType>

</xs:schema>

鉴于上述架构,以下XML文档(a.xml)显然对它有效:

<a:type xmlns:a="foo://a" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="foo://a A.xsd"
        xsi:type="a:Type">
    Organisation
</a:type>

令人费解的是,Xerces报告以下实例文档(a-v2.xml)也是有效的:

<absurdRootElementName xmlns:a="foo://a" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="foo://a A.xsd"
  xsi:type="a:Type">
    Organisation
</absurdRootElementName>

为了证明这一点,我从this link下载了Xerces2 for Java,爆炸了tarball并将以下三个jar文件放在某个位置:

  1. xercesImpl.jar
  2. XML-apis.jar
  3. xercesSamples.jar
  4. 然后我写了这个验证脚本:

    $ cat validate 
    #!/bin/bash
    XERCES_HOME=~/your-chose-location
    java -classpath $XERCES_HOME/xercesImpl.jar:$XERCES_HOME/xml-apis.jar:$XERCES_HOME/xercesSamples.jar sax.Counter $*
    

    使用:

    调用validate脚本
    validate -v -n -np -s -f a.xml
    

    ...演示Xerces验证a.xmla-v2.xml是否正确。

    相反xmllint,用:

    调用
    xmllint -schema A.xsd a.xml
    

    ...验证第一个版本,但抱怨第二个版本:

      

    架构有效性错误:元素'absurdRootElementName':验证根目录没有可用的匹配全局声明。

    我的问题是:

    • 设置xsi:type意味着我可以将根元素的名称设置为我喜欢的任何值,而不会影响XSD验证结果吗?
    • 哪个工具是正确的,Xercesxmllint

1 个答案:

答案 0 :(得分:1)

与其他一些验证语言不同,XSD没有为给定的模式定义一个简单的布尔值概念&#34;有效性#34;。所以问题的答案是&#34;这取决于。&#34;

XSD定义了几种可以请求XSD验证器来评估文档的模式有效性的方法;它并不禁止其他人:

  • 类型驱动验证:我们可以在架构中指定一个类型定义,并在文档中指定一个节点,并询问这个节点是否对此类型有效?&#34 ;

    在这种情况下,我们指定的元素节点上的xsi:type属性不会覆盖我们指定的类型。 (我差点说&#34;它没有效果&#34;但它可以:如果指定xsi:type,它的值必须命名一个实际存在于模式中的类型;如果没有顶级类型的表示QName,xsi:type属性无效,其父项无效。)

  • 元素驱动的验证:我们可以在架构中指定一个元素声明,并在文档中指定一个元素节点,并询问&#34;此元素是否对此声明有效?&# 34;

    在这种情况下,元素实例将根据我们指定的元素声明进行验证。我们指定的元素节点上的xsi:type属性将(在没有错误的情况下)覆盖指定的类型。如果在模式中找不到实例中指定的类型,或者如果找到它但是没有从声明的类型有效地派生,或者如果违反了某些其他规则,则会出现有效性问题。

  • 属性驱动的验证并不适用于您正在谈论的情况;它涉及在模式中指定属性声明和实例中的属性节点,并询问&#34;此属性实例是否对此属性声明有效?&#34;。

  • lax-wildcard validation :我们可以在文档实例中指定一个节点并询问&#34;该节点是否对模式中的声明有效,如果有的话?&#34 ;

    在lax-wildcard验证中,期望在应用程序级别,有效节点计为成功,无效节点计为失败,有效未知的节点(因为没有此类声明)计为成功。在这种情况下,验证根目录中的xsi:type属性将被视为标识管理类型定义。

  • 严格通配符验证:这基本上类似于lax-wildcard验证,除非验证根的有效性未知(因为没有管理元素声明或类型定义) ,然后它在应用程序级别计入失败。

许多命令行工具默认对XML输入中最外层元素进行松散通配符验证:它们在模式中查找它的顶级声明,如果找到它则验证该声明,并且他们找不到一个。 (这会产生奇怪的后果,如果由于命名空间错误或由于某种原因没有找到声明,它可能会使用户看起来好像文档是有效的,而不仅仅是不知道是无效的。)

在您描述的情况下,Xerces似乎默认为松散(或严格)通配符模式,找到xsi:type声明,并正确声明文档有效。 xmllint处理器似乎默认为不同的模式,与规范中描述的任何模式略有不同,其中寻找顶级元素声明并且如果找不到则发出错误消息。这与XSD规范中定义的严格通配符验证模式非常相似,但似乎排除了通过xsi:type属性验证实例中指定的类型的可能性。在这种情况下,xmllint非常正确地报告文档的根元素对模式中的任何顶级元素声明无效。

现在我们可以扩展&#34;它取决于&#34;回答更多信息。

  

设置xsi:type意味着我可以将根元素的名称设置为我喜欢的任何值,而不会影响XSD验证结果吗?

这取决于您正在执行的验证类型。

答案是&#34;是&#34;包括这些:

  • 根元素的名称永远不会对从其他节点开始的验证剧集产生任何影响,因此在您请求验证属性或除根元素之外的元素的所有情况下,您确实可以将其值设置为您喜欢的任何值。

  • 在您请求类型驱动验证的情况下,或者您请求松散或严格通配符验证并且架构没有与根元素匹配的顶级元素声明的情况下,根元素的名称将没有对模式有效性评估的影响。

答案是&#34;否&#34;:

  • 如果您请求元素驱动的验证,则根元素的名称必须与元素声明中指定的名称匹配。

  • 如果(a)您请求通配符验证并且(b)架构具有匹配的元素声明,则根元素的名称将对架构有效性评估产生影响,因为它将决定哪个选择元素声明作为控制元素声明,它将依次确定实例指定的类型是否有效地从元素的声明类型派生。

  

哪个工具是对的,Xerces还是xmllint?

这取决于你如何定义正确性。

XSD规范定义了几种请求验证的方式(在1.1中为它们分配了上面给出的名称),但很明显不会尝试将用户或应用程序接口定义为标准的一部分,因此没有&# 39;右&#39;或者&#39;错误&#39;关于验证问题的确切表述,模式验证器假设您将其称为默认验证例程。