一个xml命名空间等于一个且只有一个模式文件?

时间:2011-02-14 22:32:01

标签: xml schema xsd

...或为什么这些文件在 Visual Studio 2010 中验证,而不是在 xmllint 1 中验证?

我目前正在处理已发布的xml架构,其中原作者的习惯是将架构分解为几个.xsd文件,但某些架构文件具有相同的targetNamespace。这真的“允许”吗?

示例(极简化):

File    targetNamespace    Contents
------------------------------------------------------------
b1.xsd  uri:tempuri.org:b  complex type "fooType"
b2.xsd  uri:tempuri.org:b  simple type "barType"

a.xsd   uri:tempuri.org:a  imports b1.xsd and b2.xsd
                           definition of root element "foo", that
                           extends "b:fooType" with an attribute
                           of "b:barType"

(完整文件内容如下。)

然后我有一个xml文件data.xml,其中包含以下内容:

<?xml version="1.0"?>
<foo bar="1" xmlns="uri:tempuri.org:a" xmlns:xs="http://www.w3.org/2001/XMLSchema" />

很长一段时间以来,我一直认为所有这些都是正确的,因为Visual Studio显然允许这种架构风格。但是,今天我决定设置一个命令行实用程序来验证xml文件,我选择了xmllint

当我跑xmllint --schema a.xsd data.xml时,我收到了这个警告:

  

a.xsd:4:元素导入:架构解析器警告:元素“{http://www.w3.org/2001/XMLSchema} import”:   跳过位于'b2.xsd'的架构导入名称空间'uri:tempuri.org:b',因为这   已导入名称空间,其架构位于“b1.xsd”。

跳过b2.xsd导入的事实显然会导致此错误:

  

a.xsd:9:元素属性:模式解析器错误:属性decl。 'bar',属性'type':   QName值'{uri:tempuri.org:b} barType'未解析为(n)简单类型定义。

如果xmllint是正确的,那么我正在发布的已发布规范中会出现错误。 有吗?而Visual Studio会出错。 是吗?

我确实意识到xs:importxs:include之间的区别。现在,我只是看不出xs:include如何解决问题,因为:

  • b1.xsdb2.xsd具有相同的targetNamespace
  • 他们在targetNamespacea.xsd
  • 之间存在差异
  • 他们不(需要)了解对方

这是原始架构规范中的缺陷吗?我开始认为第三点是至关重要的。如果他们彼此不了解这一事实导致将他们放在不同的名称空间中开始吗?


b1.xsd:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="uri:tempuri.org:b" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="fooType" />
</xs:schema>

b2.xsd:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="uri:tempuri.org:b" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="barType">
    <xs:restriction base="xs:integer" />
  </xs:simpleType>
</xs:schema>

a.xsd:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="uri:tempuri.org:a" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:b="uri:tempuri.org:b">
  <xs:import namespace="uri:tempuri.org:b" schemaLocation="b1.xsd" />
  <xs:import namespace="uri:tempuri.org:b" schemaLocation="b2.xsd" />
  <xs:element name="foo">
    <xs:complexType>
      <xs:complexContent>
        <xs:extension base="b:fooType">
          <xs:attribute name="bar" type="b:barType" />
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
</xs:schema>

注意:

1)我正在libxml2/xmllint使用www.zlatkovic.com的Windows端口。

1 个答案:

答案 0 :(得分:28)

问题的关键在于当你有两个不同的<import>元素时它们的意思是什么,当它们都引用相同的命名空间时。

当您认为schemaLocation的{​​{1}}属性完全是可选的时,它有助于澄清含义。当你把它遗漏时,你只是说“我想将命名空间XYZ的模式导入这个模式”。 <import>只是提示在哪里可以找到其他模式的定义。

当您阅读W3C规范时,schemaLocation的确切含义有点模糊,可能是故意的。因此,解释各不相同。

某些XML处理器可以容忍同一命名空间的多个<import>,并且基本上将所有<import>合并到一个目标中。

其他处理器更严格,并确定每个目标命名空间只有一个schemaLocation有效。当您认为<import>是可选的时,我认为这更为正确。

除了你提供的VS和xmllint示例之外,Xerces-J也是超严格的,并忽略了同一目标命名空间的后续{​​{1}},给出了与xmllint相同的错误。另一方面,XML Spy更宽松(但是,后来,XML Spy的验证非常不稳定)

为安全起见,您应该拥有这些多次导入。给定的命名空间应该有一个“主”文档,而每个子文档又有一个schemaLocation。这个主人通常是高度人为的,仅作为一个容器。这些子文件。

从我所看到的情况来看,这通常包括XML Schema的“最佳实践”,当涉及到最大的工具兼容性时,但有些人会认为这是一种破坏优雅架构设计的方法。

咩。