Как сделать взаимоисключающие атрибуты в схеме XML?
Я пытаюсь сделать два атрибута XML взаимоисключающими. Как создать схему XSD для захвата такого сценария?
Я хотел бы иметь один из этих
<elem value="1" />
<elem ref="something else" />
но не
<elem value="1" ref="something else" />
Ответы
Ответ 1
Поскольку RelaxNG упоминался в ответе Алнитак, вот решение
с RelaxNG (язык, который в большинстве случаев лучше, чем W3C
Схема). Обратите внимание на OR (|) в определении elem:
start = document
document = element document {elem+}
elem = element elem {ref | value}
ref = attribute ref {text}
value = attribute value {xsd:integer}
Если у меня есть этот XML файл:
<document>
<elem value="1" />
<elem ref="something else" />
</document>
Принято rnv и xmlint
% rnv attributes-exclusive.rnc attributes-exclusive.xml
attributes-exclusive.xml
% xmllint --noout --relaxng attributes-exclusive.rng attributes-exclusive.xml
attributes-exclusive.xml validates
Если я добавлю в XML файл:
<elem value="1" ref="something else" />
Я получаю ошибки проверки, как я хочу (обратите внимание, что сообщения об ошибках
являются субоптимальными):
% rnv attributes-exclusive.rnc attributes-exclusive.xml
attributes-exclusive.xml
attributes-exclusive.xml:4:0: error: attribute ^ref not allowed
required:
after
% xmllint --noout --relaxng attributes-exclusive.rng attributes-exclusive.xml
attributes-exclusive.xml:4: element elem: Relax-NG validity error : Invalid attribute value for element elem
attributes-exclusive.xml fails to validate
Ответ 2
Вы не можете делать с атрибутами, но вы можете с дочерними элементами...
<element name="elem">
<complexType>
<choice>
<element name="value"/>
<element name="ref"/>
</choice>
</complexType>
</element>
Таким образом, вы можете...
<elem>
<value>1</value>
</elem>
или...
<elem>
<rel>something else</rel>
</elem>
Ответ 3
К сожалению, AFAIK вы не можете сделать это с помощью XML-схемы, у меня была та же проблема.
Я видел, что он предположил, что если вам нужны оба:
<elem type="xxx">
<elem ref="yyy">
то <elem>
сам должен быть разбит на два типа, так как они явно получили разные атрибуты...
Ответ 4
XSD имеет для этого абстрактные типы: http://www.tek-tips.com/viewthread.cfm?qid=1364846 (см. сообщение от tsuji)
В принципе, вы даете элементу под рукой абстрактный, сложный тип и определяете его общие атрибуты там, которые абсолютно одинаковы для всех разных случаев использования (не требуется для вашего примера).
Затем вы создаете 2 (или более) дополнительных сложных типа, которые расширяют абстрактный тип, о котором я только что упомянул. В рамках этих новых типов вы определяете разные наборы атрибутов между каждым вариантом использования. Что это для части XSD.
Наконец, вам нужно добавить атрибут XSI type
к результирующему элементу в экземпляре экземпляра схемы. Поэтому, чтобы быть действительным, элемент должен теперь иметь либо один набор атрибутов, либо другой.
Не прямолинейный, но гибкий, и, как мы все знаем: чем более гибким, тем труднее что-то становится.
Ответ 5
Для читателей, которые приходят к этому позже, обратите внимание, что проблема может быть решена в XSD 1.1 с использованием либо условного назначения типа, либо утверждений.
Ответ 6
Собственно, это можно определить в XSD 1.0 с использованием ограничений идентификации через xs: unique или xs: key. Какой из них вы выбираете, зависит от того, как обрабатывать элементы без одного из двух атрибутов: они действительны с xs: unique, но недействительны с помощью xs: key. Пример кода ниже содержит оба варианта; убедитесь, что удалили один из них, иначе "более строгий" xs: ключ имеет преимущество перед xs: unique, то есть один из двух атрибутов.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="container">
<xs:complexType>
<xs:sequence>
<xs:element name="elem" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="value" use="optional"/>
<xs:attribute name="ref" use="optional"/>
</xs:complexType>
<!-- Note: Use either xs:unique or xs:key -->
<xs:unique name="attrsExclusiveOptional">
<xs:annotation>
<xs:documentation>Ensure that @value and @ref cannot occur simultaneously.
Both may be omitted.</xs:documentation>
</xs:annotation>
<xs:selector xpath="."/>
<xs:field xpath="@value | @ref"/>
</xs:unique>
<xs:key name="attrsExclusiveRequired">
<xs:annotation>
<xs:documentation>Ensure that @value and @ref cannot occur simultaneously.
One of them is required.</xs:documentation>
</xs:annotation>
<xs:selector xpath="."/>
<xs:field xpath="@value | @ref"/>
</xs:key>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Это подтверждает следующий XML файл:
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schema.xsd">
<!-- Shall pass: -->
<elem value="1" />
<elem ref="something else" />
<!-- Passes for xs:unique, fails for xs:key: -->
<elem />
<!-- Shall fail: -->
<elem value="1" ref="something else" />
</container>