6

我需要解析一个 XSD,XSOM但这个 XSD 包含循环导入。

a.xsd

<xs:schema xmlns=”ns1” targetNamespace=”ns1”&gt;
  <xs:import namespace=”ns2” schemaLocation=”B.xsd”/>
  <xs:element name=”MyElement” type=”xs:string”/>
</xs:schema>

B.xsd

<xs:schema xmlns=”ns2” targetNamespace=”ns2” xmlns:ns1=”ns1”&gt;
  <xs:import namespace=”ns1” schemaLocation=”A.xsd”/>
  <xs:complexType name="MyComplex">
    <xs:sequence>
      <xs:element ref="ns1:MyElement" minOccurs="0"/>
    <xs:sequence>
  <xs:complexType>
</xs:schema>

XSOM无法解析架构,因为它检测到由于循环导入而已定义的元素。所以我试图通过外部化由 A 定义并在 B 中使用的元素来打破循环导入。

C.xsd 包含 B 使用的来自 A 的元素。请注意,这些元素在 A 中没有使用。不要问我为什么在 A 中定义了这些元素。

<xs:schema xmlns=”ns1” targetNamespace=”ns1”&gt;
  <xs:element name=”MyElement” type=”xs:string”/>
</xs:schema>

A.xsd 变成

<xs:schema xmlns=”ns1” targetNamespace=”ns1”&gt;
  <xs:import namespace=”ns2” schemaLocation=”B.xsd”/>
</xs:schema>

B.xsd(导入 C.xsd 而不是 A.xsd)变为

<xs:schema xmlns=”ns2” targetNamespace=”ns2” xmlns:ns1=”ns1”&gt;
  <xs:import namespace=”ns1” schemaLocation=”C.xsd”/>
  <xs:complexType name="MyComplex">
    <xs:sequence>
      <xs:element ref="ns1:MyElement" minOccurs="0"/>
    <xs:sequence>
  <xs:complexType>
</xs:schema>

XSOM可以解析XSD。但现在我无法使用以下代码创建架构:

SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
sf.setResourceResolver(new MyResourceResolver());

我使用与 JDK 1.7 捆绑在一起的标准实现。我得到了例外:

src-resolve: Cannot resolve the name 'ns1:MyElement' to a(n) 'element declaration' component.

问题是为 B 命名空间调用了资源解析器,而不是为有意义的 A 命名空间调用了资源解析器。由于命名空间 A 由 A.xsd 和 C.xsd 共享,因此资源解析器无法找到 C.xsd 中定义的元素。

循环进口是否有效?是否可以打破循环导入,以便它可以被解析XSOM然后被加载SchemaFactory

4

1 回答 1

8

关于一般问题:

您问“循环进口是否有效?” 如果循环性是指存在模式文档链 S[1]、S[2]、...、S[n],其中模式文档 S[1] 按名称引用模式文档 S[2],S [2] 到 S[3],... S[n-1] 到 S[n],和 S[n] 到 S[1] 那么我不相信 XSD 1.0 规范或 XSD 1.1 说清楚一种或另一种方式。(一些 WG 成员试图说服 WG 提高其对该主题和相关主题的思考的清晰度,但失败了。)一些实现支持循环导入(和其他形式的循环),但我认为不可能争论从规范来看,您的实现做错了什么。

另一方面,如果您的意思仅仅是存在一个循环,对于 0 <= i <= n-1,S[i] 导入 S[i+1] 的命名空间,而 S[n] 导入 S 的命名空间[1],那么我相信这样的循环显然是合法的(在某些情况下是不可避免的)。

我推荐的解决方法是:

  1. 在任何声明任何内容的架构文档中,根据需要使用 xs:import,但不要在导入时指定架构位置。此类引用中的循环是无害的。
  2. 调用模式处理器时,给它你希望它读取的所有模式文档的完整列表,如果可能的话,通过选项或配置告诉它不要读取任何其他模式文档。
  3. 如果您的架构文档在验证时不接受多个架构文档作为输入,那么您必须有一个架构文档来引用您想要阅读的所有内容,或者如果您不相信自己能够正确获取架构文档列表在调用时,然后添加一个顶级驱动程序文档,该文档除了包含和导入您想要读取的其他架构文档之外什么都不做,并带有特定的架构位置信息。

在您的情况下,这意味着从 A.xsd 和 B.xsd 的(原始形式)中删除 xs:import/@schemaLocation 属性,并添加形式为的驱动程序文档

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:import namespace="ns1" schemaLocation="A.xsd"/>
  <xs:import namespace="ns2" schemaLocation="B.xsd"/>
</xs:schema>

其效果是确保模式文档对其他模式文档的引用永远不会出现循环;这消除了一大类 XSD 实现相互不一致的情况(在某些情况下与它们自己不一致——有时当调用以不同的顺序命名输入时,相同的处理器会在相同的输入上产生显着不同的结果)。

关于具体问题:

在您的示例中,不要求 ns2 由 A.xsd 或 C.xsd 导入,因为它们都不包含对命名空间 ns2 中任何组件的任何引用。因此,您示例中的循环似乎是无缘无故的。

在您的第二个示例中,您提供了一些无法成功加载架构的代码。但是我在该代码中根本没有看到任何对任何特定模式文档的引用;除非您没有向我们展示相关的内容,否则验证器找不到 {ns1}MyElement 的声明也就不足为奇了。

于 2013-02-12T19:41:46.050 回答