Схема XML для элемента, простой тип которого определяется атрибутом

Я хочу написать XML-схему (xsd 1.1) для документа, содержащего параметры. У каждой опции есть имя и тип (например, логическое значение, целое число, строка и т. д.) и данные, соответствующие этому типу. Список типов фиксированный, но довольно длинный. (Только 3 перечислены в листинге 3 для простоты.)

Как мне сделать это без нелепого количества повторений?

Вариант использования 1

Вот действительный документ для этой схемы.

Листинг 1:

<abc:options>
  <abc:option name="is-enabled" type="boolean">false</abc:option>
  <abc:option name="wing-span"  type="float">1.2</abc:option>
</abc:options>

Вариант использования 2

Этот документ недействителен для этой схемы, поскольку бит простого типа неверен для атрибута @type.

<abc:options>
  <abc:option name="is-enabled" type="boolean">24</abc:option>
  <abc:option name="wing-span"  type="float">this-is-not-a-number!</abc:option>
</abc:options>

То, что я пробовал до сих пор ...

Листинг 3 — моя попытка на данный момент. Но это плохо, потому что мне приходится повторно объявлять атрибут @name для каждого типа данных. Есть ли лучшее решение? Другими словами, мне не нужно повторно объявлять атрибут @name для каждого возможного типа данных.

Листинг 3:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:abc="http://www.example.com"
           targetNamespace="http://www.example.com"
           elementFormDefault="qualified"
           attributeFormDefault="unqualified">

  <xs:element name="options">
    <xs:complexType>
      <xs:sequence minOccurs="1" maxOccurs="unbounded">
        <xs:element name="abc:option" type="option-Type"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:simpleType name="option-Datum-Type">
    <xs:restriction base="xs:string">
      <xs:enumeration value="boolean"/>
      <xs:enumeration value="integer"/>
      <xs:enumeration value="float"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:complexType name="option-Type-boolean">
    <xs:simpleContent>
      <xs:extension base="xs:boolean">
        <xs:attribute name="name" type="xs:token" use="required" />
        <xs:attribute name="type" type="abc:option-Datum-Type" use="required" />
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:complexType name="option-Type-string">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="name" type="xs:token" use="required" />
        <xs:attribute name="type" type="abc:option-Datum-Type" use="required" />
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:complexType name="option-Type-float">
    <xs:simpleContent>
      <xs:extension base="xs:double">
        <xs:attribute name="name" type="xs:token" use="required" />
        <xs:attribute name="type" type="abc:option-Datum-Type" use="required" />
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:complexType name="option-Type">
    <xs:alternative test="@type='boolean'"  type="abc:option-Type-boolean"/>
    <xs:alternative test="@type='string'"   type="abc:option-Type-string" />
    <xs:alternative test="@type='float'"    type="abc:option-Type-float"  />
    <xs:alternative type="xs:error"/>
  </xs:complexType>

</xs:schema>

person Sean B. Durkin    schedule 12.11.2015    source источник
comment
Более экономным решением было бы заменить имя элемента option тем, что у вас сейчас есть в option/@name, а затем использовать, а не изобретать заново, xsi:type, например это. Дайте мне знать, если вы заинтересованы в том, чтобы этот подход применялся в ответе здесь.   -  person kjhughes    schedule 12.11.2015
comment
Пробовали ли вы поместить XML-документ XML через xsd и сгенерировать схема?   -  person Jeremy Thompson    schedule 13.11.2015
comment
@JeremyThompson Visual Studio не поддерживает xsd 1.1, который необходим для этой структуры.   -  person Sean B. Durkin    schedule 13.11.2015


Ответы (1)


Если тип может быть только одним из атомарных типов, вы можете использовать xs:assert следующим образом:

<xs:complexType name="option-Type">
    <xs:simpleContent>
        <xs:extension base="xs:string">
            <xs:attribute name="name" type="xs:token" use="required" />
            <xs:attribute name="type" type="xs:string" use="required" />
            <xs:assert
                test="if (@type='boolean')  then . castable as xs:boolean
                else if (@type='float')     then . castable as xs:float
                else if (@type='int')       then . castable as xs:int
                else if (@type='string')    then . castable as xs:string
                else false()"/>
        </xs:extension>
    </xs:simpleContent>
</xs:complexType>

Примечания:

  • Вам не нужно объявлять какой-либо новый тип. Если вы хотите, вы можете даже пропустить объявление перечисления.

  • Используя этот подход, вам нужна новая строка для каждого нового возможного типа (на самом деле вам не нужна новая строка, но ее легко читать с каждым типом в отдельной строке).

  • Вы можете использовать text() вместо ., если это кажется вам более понятным.

  • Обратите внимание, насколько простым был бы этот подход, если бы в XPath 2.0 была функция eval, аналогичная функции javascript и eval в других языках:

    <xs:assert test="eval(text() || ' castable as xs:' || @type)"/>
    

    Я думал, что функция eval/parse должна была быть добавлена ​​в спецификацию XPath 3.0, но я думаю, что в конце концов она не была добавлена.

  • В отличие от instance of вы не можете использовать списки (*,+), отличные от ?, с оператором castable as. Используя этот подход, вы можете использовать только атомарные типы.

  • Преобразование в строку всегда должно выполняться успешно, так как тип объявлен как xs:string.

person sergioFC    schedule 21.11.2015