1

我正在尝试验证一个依赖于另一个实例(来自不同命名空间)的 XML 实例,并且它具有该命名空间中一个键的 keyref。当我尝试验证实例时,它会产生一个错误,指出密钥超出范围。

这些是我的 XSD:

test1.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
    targetNamespace="test1" xmlns="test1">

    <xs:complexType name="Host">
        <xs:attribute name="id" type="xs:string"/>
    </xs:complexType>
    <xs:element name="root">
        <xs:complexType>
            <xs:all>
                <xs:element name="hosts">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element maxOccurs="unbounded" minOccurs="0" name="host" type="Host"
                            />
                        </xs:sequence>
                    </xs:complexType>
                    <xs:key name="Host-PK">
                        <xs:selector xpath="host"/>
                        <xs:field xpath="@id"/>
                    </xs:key>
                </xs:element>
            </xs:all>
        </xs:complexType>
    </xs:element>
</xs:schema>

test2.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
    targetNamespace="test2" xmlns="test2" xmlns:t1="test1">

    <xs:import namespace="test1" schemaLocation="test1.xsd"/>

    <xs:element name="root">
        <xs:complexType>
            <xs:all>
                <xs:element name="server">
                    <xs:complexType>
                        <xs:attribute name="host" type="xs:string"/>
                    </xs:complexType>
                </xs:element>
            </xs:all>
        </xs:complexType>
        <xs:keyref name="Host-FK" refer="t1:Host-PK">
            <xs:selector xpath="server"/>
            <xs:field xpath="@host"/>
        </xs:keyref>
    </xs:element>
</xs:schema>

还有我的例子:

测试1.xml

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="test1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="test1 test1.xsd">
  <hosts>
    <host id="ABC"/>
    <host id="DEF"/>
  </hosts>
</root>

test2.xml

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:t1="test1" xmlns="test2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="test2 test2.xsd">
 <server host="ABC"/>
</root>

服务器主机属性是对主机 ID 的关键引用。

在模式验证期间,test2.xml文件引发了以下错误:

错误:[Xerces] 身份约束错误:keyref 身份约束“Host-FK”指的是超出范围的键或唯一键。

我该如何解决?

以及如何从test2.xml引用test1.xml实例?

4

1 回答 1

1

我假设您可以更改两个 XSD,并且您使用的是 XSD 1.0。

在第一个 XSD 中,您需要限定 XPath 元素,因为无前缀元素不属于任何 namespace。因为是你的钥匙不起作用。您可以验证这一点,将重复的 ID 添加到test1

<root xmlns="test1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="test1 test1.xsd">
    <hosts>
        <host id="ABC"/>
        <host id="DEF"/>
        <host id="DEF"/>
    </hosts>
</root>

它仍然验证,当它不应该。

要解决此问题,请添加第二个test1带有前缀的命名空间声明:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
    targetNamespace="test1" xmlns="test1" xmlns:t1="test1">

现在您可以限定您的 XPath 表达式:

<xs:key name="Host-PK">
    <xs:selector xpath="t1:host"/>
    <xs:field xpath="@id"/>
</xs:key>

正如预期的那样,重复 ID 的验证将失败。

现在您的第二个 XSD 将无法找到任何Host-PK. 它的root元素是完全不同的元素。root它只是与of共享相同的名称test1。不可能在范围内。如果您想在两个模式中共享相同的键,您可以做的一件事是将 in 声明为inroottest2扩展。但这需要进行一些更改才能允许引用.roottest1test1.xsdtest2test1.xsd

要允许其他模式扩展root元素的类型,请将其设为顶级。另外,使hosts元素成为顶级元素,因为我们需要引用它来定义keyref. 您还可以使用它来验证具有hosts作为根元素的文件(这将很有用,我们将在前面看到)。

我们将无法扩展xs:all,但在您的情况下,您可以安全地将其替换为xs:sequence. 这是重构后的最终test1.xsd

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           elementFormDefault="qualified"
           targetNamespace="test1" 
           xmlns="test1" 
           xmlns:t1="test1">

    <xs:complexType name="Host">
        <xs:attribute name="id" type="xs:string"/>
    </xs:complexType>

    <xs:complexType name="Root">
        <xs:sequence>
            <xs:element ref="hosts" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>

    <xs:element name="root" type="Root" />

    <xs:element name="hosts">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" minOccurs="0" name="host" type="Host"/>
            </xs:sequence>
        </xs:complexType>
        <xs:key name="Host-PK">
            <xs:selector xpath="t1:host"/>
            <xs:field xpath="@id"/>
        </xs:key>
    </xs:element>

</xs:schema>

我还添加minOccurs="0"了,hosts所以可以定义一个root只包含一个服务器的(但这是临时的 - 我们将在完成之前再次要求它)

现在我们可以引用hosts元素和Root类型test2.xsd。我们可以从扩展root元素的基本类型开始,以允许server元素:

<xs:complexType name="NewRoot">
    <xs:complexContent>
        <xs:extension base="t1:Root">
            <xs:sequence>
                <xs:element name="server">
                    <xs:complexType>
                        <xs:attribute name="host" type="xs:string"/>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>

这也是一个序列。

root元素应声明为:

<xs:element name="root" type="NewRoot"> ... </xs:element>

现在root元素 intest2是元素 in 的扩展roottest1并且host元素将在上下文中。

由于我们必须使用 XPath 来选择服务器元素,因此需要为命名空间声明前缀,test2以便我们可以在 XPath 表达式中使用它:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
           xmlns:t1="test1" targetNamespace="test2" 
           xmlns="test2" xmlns:t2="test2" >

现在您可以定义一个本地键来引用hosts/host并将其用于 中的host属性server

<xs:element name="root" type="t2:NewRoot">
    <xs:key name="Host-PK">
        <xs:selector xpath="t1:hosts/t1:host"/>
        <xs:field xpath="@id"/>
    </xs:key>
    <xs:keyref name="Host-FK" refer="Host-PK">
        <xs:selector xpath="t2:server"/>
        <xs:field xpath="@host"/>
    </xs:keyref>
</xs:element>

这是重构后的最终test2.xsd

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           elementFormDefault="qualified"
           targetNamespace="test2" 
           xmlns="test2" 
           xmlns:t2="test2" 
           xmlns:t1="test1">

    <xs:import namespace="test1" schemaLocation="test1.xsd"/>

    <xs:complexType name="NewRoot">
        <xs:complexContent>
            <xs:extension base="t1:Root">
                <xs:sequence>
                    <xs:element name="server">
                        <xs:complexType>
                            <xs:attribute name="host" type="xs:string"/>
                        </xs:complexType>
                    </xs:element>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:element name="root" type="t2:NewRoot">
        <xs:key name="Host-PK">
            <xs:selector xpath="t1:hosts/t1:host"/>
            <xs:field xpath="@id"/>
        </xs:key>
        <xs:keyref name="Host-FK" refer="Host-PK">
            <xs:selector xpath="t2:server"/>
            <xs:field xpath="@host"/>
        </xs:keyref>
    </xs:element>
</xs:schema>

所以现在你尝试验证test2.xml并且......它失败了,但不再出现“超出范围”错误。它失败了,因为它没有找到任何具有该ABC值的键。这意味着它正在验证密钥,但它无法访问host元素。您需要将它们放在您的 XML 实例中。

hosts如果您只是从 中剪切和粘贴元素test1.xml并为它们设置默认命名空间,它将起作用:

<root xmlns:t1="test1" xmlns="test2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="test2 test2.xsd">
    <hosts xmlns="test1">
        <host id="ABC"/>
        <host id="DEF"/>
    </hosts>
    <server host="ABC"/>
</root>

你可以试试看。host除非isABC或否则它不会验证DEF

您可能还希望将hosts子树保存在单独的文件中,并将其导入到您的两个 XML 实例中。这样做的本机方法是声明一个 DTD 实体。首先将您hosts的文件(test3.xml):

<hosts xmlns="test1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="test1 test1.xsd">
    <host id="ABC"/>
    <host id="DEF"/>
</hosts>

现在将其包含到test1.xmltest2.xml使用<!ENTITY>

测试1.xml

<!DOCTYPE root [
   <!ENTITY test3 SYSTEM "test3.xml">
]>

<root xmlns="test1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="test1 test1.xsd">
    &test3;
</root>

test2.xml

<!DOCTYPE root [
   <!ENTITY test3 SYSTEM "test3.xml">
]>

<root xmlns:t1="test1" xmlns="test2" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="test2 test2.xsd">
    &test3;
    <server host="ABC"/>
</root>

现在您可以将minOccurs="0"back 放在 in 的声明hoststest1.xsd,以保证它始终存在。

于 2014-04-22T21:53:35.580 回答