Как я могу создать экземпляр 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
, чтобы гарантировать, что он всегда будет присутствовать.