如何使用包含来自另一个名称空间的keyref的XSD创建XML实例 [英] How can I create a XML instance with a XSD that includes keyref from another namespace

查看:93
本文介绍了如何使用包含来自另一个名称空间的keyref的XSD创建XML实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试验证依赖于另一个实例(来自不同名称空间)的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元素是完全不同的元素.它只是与test1root共享相同的名称.它不可能在范围内.如果要在两个模式中共享相同的密钥,则可以做的一件事情是声明test2中的root作为test1root的扩展.但这需要对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元素是test1root元素的扩展,并且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>

您可以尝试一下.除非hostABCDEF,否则它不会生效.

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.xmltest2.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.xsdhosts的声明中,以确保它始终存在.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆