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

does specifying xsi:type allow one to change the root element's name to any value?

我在试验 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 下载了 Java 的 Xerces2,解压了 tarball 并将以下三个 jar 文件放在特定位置:

  1. xercesImpl.jar
  2. xml-apis.jar
  3. xercesSamples.jar

然后我写了这个验证脚本:

$ 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

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

Schemas validity error : Element 'absurdRootElementName': No matching global declaration available for the validation root.

我的问题是:

与其他一些验证语言不同,XSD 没有定义“针对给定模式的有效性”的单一、简单的布尔值概念。所以你的问题的答案是“视情况而定。”

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

  • 类型驱动的验证:我们可以在模式中指定一个类型定义,在文档中指定一个节点,然后询问“这个节点是否对这种类型有效?

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

  • 元素驱动的验证:我们可以在模式中指定一个元素声明,在文档中指定一个元素节点,然后询问“这个元素是否对这个有效声明?

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

  • 属性驱动验证不适用于您所说的情况;它涉及在模式中指定一个属性声明,在实例中指定一个属性节点,并询问“这个属性实例是否对这个属性声明有效?”。

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

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

  • strict-wildcard validation: 这本质上类似于 lax-wildcard 验证,除了如果验证根的有效性未知(因为没有管理元素声明或类型定义),则在应用程序级别计为失败。

许多命令行工具默认对 XML 输入中的最外层元素进行松散通配符验证:它们在模式中查找其顶级声明,如果找到则根据该声明进行验证一个,如果找不到就保持沉默。 (这会导致奇怪的结果,如果由于命名空间错误或出于任何原因而没有找到声明,它可能会让用户看起来好像文档是有效的,而不是仅仅不知道它是无效的。)

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

现在我们可以更丰富地扩展“视情况而定”的答案。

does setting the xsi:type mean I can set the root element's name to any value I like without impacting XSD validation outcome?

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

答案为“是”的情况包括:

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

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

回答“否”的案例:

  • 在请求元素驱动验证的情况下,根元素的名称必须与元素声明中给出的名称相匹配。

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

which tool is right, Xerces or xmllint?

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

XSD 规范定义了几种请求验证的方法(并在 1.1 中为它们分配了上面给出的名称),但它非常清楚地没有尝试将用户或应用程序界面定义为标准的一部分,因此,关于模式验证器在您将其称为默认验证例程时假定的验证问题的确切表述,没有 'right' 或 'wrong'。