我正在尝试验证依赖于另一个实例(来自不同的命名空间)的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>
我的实例:
test1.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] Identity Constraint错误:keyref标识 约束&#34; Host-FK&#34;是指超出范围的密钥或唯一。
我该如何解决?
如何从 test2.xml 引用 test1.xml 实例?
答案 0 :(得分:1)
我假设您可以更改两个XSD,并且您正在使用XSD 1.0。
在第一个XSD中,您需要限定XPath元素,因为无前缀元素属于 no namespace 。因为你的钥匙将无法正常工作。您可以验证这是否为test1
添加了重复的ID:
<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
的{{1}}共享相同的名称。它不可能在范围内。如果您想在两个模式中共享相同的密钥,您可以做的一件事就是将test1
中的root
声明为test2
中root
的扩展名。但这需要在test1
中进行一些更改,以便test1.xsd
能够引用test2
中的元素和类型。
要允许其他模式扩展test1.xsd
元素的类型,请将其设置为顶级。另外,将root
元素置于顶级,因为我们需要引用它才能定义hosts
。您还可以使用它来验证以keyref
为根元素的文件(这将非常有用,因为我们将会提前看到)。
我们无法扩展hosts
,但在您的情况下,您可以安全地将其替换为xs:all
。这是重构后的最终 test1.xsd :
xs:sequence
我还将<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
元素是test2
中root
元素的扩展名,test1
元素将位于上下文中。
由于我们必须使用XPath来选择服务器元素,因此必须为host
的命名空间声明前缀,以便我们可以在XPath表达式中使用它:
test2
现在,您可以定义引用<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
这是重构后的最终 test2.xsd :
<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 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>
并且......它失败了,但不再是&#34;超出范围&#34;错误。它失败了,因为它没有找到test2.xml
值的任何键。这意味着它可以正确验证密钥,但无法访问ABC
元素。您需要在XML实例中使用它们。
如果您只是剪切并粘贴host
中的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
或ABC
,否则无法验证。
您可能还希望将DEF
子树保留在单独的文件中,并将其导入到两个XML实例中。本机方法是声明DTD实体。首先将hosts
放在文件中( test3.xml ):
hosts
现在使用<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.xml
和test2.xml
:
<强> test1.xml 强>
<!ENTITY>
<强> test2.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>
现在,您可以将<!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"
中hosts
的声明中,以保证它始终存在。