Как сделать взаимоисключающие атрибуты в схеме 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>