如何使用包含来自另一个名称空间的keyref的XSD创建XML实例 [英] How can I create a XML instance with a XSD that includes keyref from another namespace
问题描述
我正在尝试验证依赖于另一个实例(来自不同名称空间)的XML实例,并且该实例具有指向该名称空间中的键的keyref.当我尝试验证实例时,它会产生一个错误,指出密钥不在范围内.
I am trying to validate a XML instance that depends on another instance (from a different namespace), and it has a keyref to a key in that namespace. When I try to validate the instance, it produces an error that says the key is out of scope.
这些是我的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的关键引用.
The server host attribute is a key reference to the host ids.
在模式验证期间, test2.xml 文件引发了以下错误:
During schema validation the test2.xml file raised the error below:
错误:[Xerces]身份约束错误:keyref身份 约束"Host-FK"是指超出范围的键或唯一.
Error: [Xerces] Identity Constraint error: the keyref identity constraint "Host-FK" refers to a key or unique that is out of scope.
我该如何解决?
如何从 test2.xml 引用 test1.xml 实例?
推荐答案
我假设您可以同时更改两个XSD,并且您正在使用XSD 1.0.
I'm assuming you can change both XSDs, and that you are using XSD 1.0.
在第一个XSD中,您需要限定XPath元素,因为无前缀元素不属于命名空间.如是,您的钥匙将无法使用.您可以验证此操作是否将重复的ID添加到test1
:
In the first XSD, you will need to qualify your XPath elements, since unprefixed elements belong to no namespace. As is your key will not work. You can verify this adding a duplicate ID to 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>
它应该验证,何时不应该验证.
It still validates, when it shouldn't.
要解决此问题,请添加第二个带有前缀的test1
名称空间声明:
To fix that, add a second test1
namespace declaration with a prefix:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="test1" xmlns="test1" xmlns:t1="test1">
现在,您可以限定您的XPath表达式了:
Now you can qualify your XPath expression:
<xs:key name="Host-PK">
<xs:selector xpath="t1:host"/>
<xs:field xpath="@id"/>
</xs:key>
按预期,重复ID的验证将失败.
And the validation for the duplicate ID will fail, as expected.
现在,您的第二个XSD将找不到任何Host-PK
.它的root
元素是完全不同的元素.它只是与test1
的root
共享相同的名称.它不可能在范围内.如果要在两个模式中共享相同的密钥,则可以做的一件事情是声明test2
中的root
作为test1
中root
的扩展.但这需要对test1.xsd
进行一些更改,以允许test2
引用test1.xsd
中的元素和类型.
Now your second XSD won't be able to find any Host-PK
. Its root
element is a completely different one. It just shares the same name with the root
of test1
. There's no way it could be in scope. If you want to share the same key in both schemas one thing you could do is to declare the root
in test2
as an extension of the root
in test1
. But that will require some changes in test1.xsd
in order to allow test2
to refer to elements and types in test1.xsd
.
要允许其他模式扩展root
元素的类型,请将其设为顶级.同样,将hosts
元素设为顶级,因为我们需要引用它来定义keyref
.您也可以使用它来验证以hosts
作为根元素的文件(这将很有用,我们将在后面看到).
To allow other schemas to extend the type of the root
element, make it top-level. Also, make the hosts
element top-level, since we will need to refer to it in order to define the keyref
. You can also use it to validate a file which has hosts
as a root element (this will be useful, as we'll see ahead).
我们将无法扩展xs:all
,但是对于您而言,您可以安全地将其替换为xs:sequence
.这是重构后的最终 test1.xsd :
We won't be able to extend xs:all
, but in your case you can safely replace it with a xs:sequence
. This is the final test1.xsd after refactoring:
<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>
我还在hosts
中添加了minOccurs="0"
,因此可以定义仅包含服务器的root
(但这是临时的-在完成操作之前将再次需要它)
I also added minOccurs="0"
to hosts
so it will be possible to define a root
containing only a server (but this is temporary - we will make it required again before we finish)
现在,我们可以引用hosts
元素和test2.xsd
中的Root
类型.我们可以从扩展root
元素的基本类型开始,以允许server
元素:
Now we can refer to the hosts
element and to the Root
type in test2.xsd
. We can start by extending the root
element's base type, to allow for the server
element:
<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>
现在,test2
中的root
元素是test1
中root
元素的扩展,并且host
元素将处于上下文中.
Now the root
element in test2
is an extension of the root
element in test1
, and the host
element will be in context.
由于我们必须使用XPath选择服务器元素,因此有必要为test2
的命名空间声明一个前缀,以便可以在XPath表达式中使用它:
Since we have to use XPath to select the server element, it's necessary to declare a prefix for the namespace of test2
so we can use it in the XPath expression:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:t1="test1" targetNamespace="test2"
xmlns="test2" xmlns:t2="test2" >
现在,您可以定义引用hosts/host
的本地键,并将其用于server
中的host
属性:
Now you can define a local key that refers to hosts/host
and use it for the host
attribute in 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实例中.
So now you try to validate test2.xml
and ... it fails, but no longer with the "out of scope" error. It fails because it didn't find any key with the ABC
value. That means it's validating the key all right, but it can't access the host
elements. You need to have them in your XML instance.
如果您只是从test1.xml
中剪切并粘贴hosts
元素并为其设置默认名称空间,它将起作用:
It will work if you simply cut and paste the hosts
element from the test1.xml
and set a default namespace for them:
<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
,否则它不会生效.
You can try it out. It won't validate unless host
is ABC
or DEF
.
您可能还希望将hosts
子树保存在单独的文件中,并将其导入两个XML实例中.做到这一点的本机方法是声明DTD实体.首先将hosts
放在文件( test3.xml )中:
You might also want to keep the hosts
subtree in a separate file, and import it into both your XML instances. The native way to do that is declaring a DTD entity. First place your hosts
in a file (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>
现在使用<!ENTITY>
将其包含在test1.xml
和test2.xml
中:
Now include it into test1.xml
and test2.xml
using an <!ENTITY>
:
test1.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"
放回test1.xsd
中hosts
的声明中,以确保它始终存在.
Now you can place the minOccurs="0"
back in the declaration for hosts
in test1.xsd
, to guarantee that it will always be present.
这篇关于如何使用包含来自另一个名称空间的keyref的XSD创建XML实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!