Как я могу создать экземпляр XML с XSD, который включает keyref из другого пространства имен

Я пытаюсь проверить экземпляр XML, который зависит от другого экземпляра (из другого пространства имен), и у него есть ключ к ключу в этом пространстве имен. Когда я пытаюсь проверить экземпляр, он выдает ошибку, в которой говорится, что ключ находится вне области видимости.

Это мои 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>

Атрибут хоста сервера является ключевой ссылкой на идентификаторы хоста.

Во время проверки схемы файл test2.xml выдал ошибку ниже:

Ошибка: [Xerces] Ошибка ограничения идентификатора: ограничение идентификатора ключевой ссылки "Host-FK" относится к ключу или уникальному, который находится вне области действия.

Как я могу это исправить?

И как я могу сослаться на экземпляр test1.xml из test2.xml?

1 ответ

Я предполагаю, что вы можете изменить оба XSD и что вы используете XSD 1.0.

В первом XSD вам нужно будет квалифицировать элементы XPath, так как элементы без префиксов не принадлежат пространству имен. Как твой ключ не сработает. Вы можете проверить это, добавив дубликат 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>

Проверка правильности дублированного идентификатора завершится неудачно, как и ожидалось.

Теперь ваш второй XSD не сможет найти Host-PK, это root элемент совершенно другой. Он просто имеет одно и то же имя с root из test1, Там нет, как это может быть в объеме. Если вы хотите использовать один и тот же ключ в обеих схемах, вы можете объявить root в test2 как продолжение root в test1, Но это потребует некоторых изменений в test1.xsd для того, чтобы позволить test2 ссылаться на элементы и типы в test1.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 элемент в test2 является продолжением root элемент в test1 и 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 является ABC или же 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.xml а также test2.xml используя <!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" назад в декларации для hosts в test1.xsd, чтобы гарантировать, что он всегда будет присутствовать.

Другие вопросы по тегам