Как расширить выбор complexType без последовательности выбора? - программирование
Подтвердить что ты не робот

Как расширить выбор complexType без последовательности выбора?

У меня есть выбор complexType с именем abType:

<xs:complexType name="abType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="a"/>
        <xs:element name="b"/>
    </xs:choice>
</xs:complexType>

Этот тип может использоваться для создания элементов с узлами a и b в любом порядке, например:

<ab>
    <b/>
    <a/>
</ab>

Теперь я хочу создать производный тип с именем abcType, чтобы позволить узлам a, b и c в любом порядке. Поэтому я создал новый complexType на основе abType:

<xs:complexType name="abcType">
    <xs:complexContent>
        <xs:extension base="abType">
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element name="c"/>
            </xs:choice>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>

После этого я создал abc node:

<abc>
    <c/>
    <b/>
    <a/>
</abc>

Но это node недействительно! Недействительно помещать a или b после c. Причина в том, что получение типа из базового типа создает последовательность имплицитов, хотя оба типа являются выборами. XMLspy иллюстрирует это следующим образом:

Extended Choice Type

Этот результат совершенно бесполезен для типов выбора.

Итак, мой вопрос: как расширить тип выбора без выбора выбора?

Вот полный XSD и тестовый XML файл для воспроизведения проблемы:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="root">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="ab"/>
            <xs:element ref="abc"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
<xs:element name="ab" type="abType"/>
<xs:complexType name="abType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="a"/>
        <xs:element name="b"/>
    </xs:choice>
</xs:complexType>
<xs:element name="abc" type="abcType"/>
<xs:complexType name="abcType">
    <xs:complexContent>
        <xs:extension base="abType">
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element name="c"/>
            </xs:choice>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>
</xs:schema>

Пример:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="inherit-choice.xsd">
<ab>
    <b/>
    <a/>
</ab>
<abc>
    <c/>
    <b/>
    <a/>
</abc>
</root>
4b9b3361

Ответ 1

К сожалению, короткий ответ NO, вы не можете продлить выбор композитора. Логически, если есть какая-то связь между a, b и c (как в Java,.NET, все в конечном счете является объектом, вы можете сделать то же самое в XSD), то я предлагаю вместо этого использовать группы замещения (или, если хотите, что-то основано на xsi: type).

UPDATE с примером. XSD-1:

<?xml version="1.0" encoding="utf-8" ?>
<!--W3C Schema generated by QTAssistant/W3C Schema Refactoring Module (http://www.paschidev.com)-->
<xsd:schema targetNamespace="http://tempuri.org/XMLSchema.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:element name="ab" type="abType"/>

    <xsd:complexType name="abType">
        <xsd:sequence>
            <xsd:element ref="ExtensibleChoice-A" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:element name="ExtensibleChoice-A" type="ExtensibleChoiceBaseType" abstract="true" />

    <xsd:complexType name="ExtensibleChoiceBaseType" abstract="true">
        <xsd:sequence/>
    </xsd:complexType>

    <xsd:element name="a" substitutionGroup="ExtensibleChoice-A" type="aType" block="#all"/>
    <xsd:element name="b" substitutionGroup="ExtensibleChoice-A" type="bType" block="#all"/>
    <xsd:element name="c" substitutionGroup="ExtensibleChoice-A" type="cType" block="#all"/>

    <xsd:complexType name="aType">
        <xsd:complexContent>
            <xsd:extension base="ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="aChild" type="xsd:string"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
    <xsd:complexType name="bType">
        <xsd:complexContent>
            <xsd:extension base="ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="bChild" type="xsd:int"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
    <xsd:complexType name="cType">
        <xsd:complexContent>
            <xsd:extension base="ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="cChild" type="xsd:string"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema>

Members of the substitution group

Расширяемость заключается в том, что в определенный момент времени у вас могут быть только a, b и c. Если вы или потребитель решили добавить что-то (например, d), тогда вы просто создаете другую схему, которая ссылается на старую, с новым элементом d а затем использовать эту новую схему. Старый XSD файл не затрагивается; создание новых классов JAXB (в качестве примера) приведет к обратному совместимому коду.

Итак, XSD-1 будет проверять что-то вроде этого:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<ab xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
    <a>
        <aChild>aChild1</aChild>
    </a>
    <b>
        <bChild>1</bChild>
    </b>
    <c>
        <cChild>cChild1</cChild>
    </c>
</ab>

Sample XML for XSD-1

Вам понадобится нечто подобное (XSD-2):

<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema targetNamespace="http://tempuri.org/XMLSchema1.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema1.xsd" xmlns:b="http://tempuri.org/XMLSchema.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:import namespace="http://tempuri.org/XMLSchema.xsd" schemaLocation="XSD-1.xsd"/>

    <xsd:element name="d" substitutionGroup="b:ExtensibleChoice-A" type="dType" block="#all"/>

    <xsd:complexType name="dType">
        <xsd:complexContent>
            <xsd:extension base="b:ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="dChild" type="xsd:string"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema>
  • диаграмма показывает "новый" список членов, d выделен синим цветом:

Extended substitution group members list

Чтобы проверить это:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<ab xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:d="http://tempuri.org/XMLSchema1.xsd">
    <a>
        <aChild>aChild1</aChild>
    </a>
    <d:d>
        <d:dChild>1</d:dChild>
    </d:d>
</ab>

Sample XML showing the new member

Ответ 2

Существует способ сделать это, полагаясь на то, что выбор в рамках выбора действует как более широкий выбор.

Сначала определите группу элементов, которая содержит единственный выбор из всех элементов в базовом элементе:

<xs:group name="common_ab_elements">
    <xs:choice>
        <xs:element name="a"/>
        <xs:element name="b"/>
    </xs:choice>
</xs:group>

Затем вы можете использовать это в своем определении abElement вместо элементов, которые у вас были до этого:

<xs:complexType name="abType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:group ref="common_ab_elements"/>
    </xs:choice>
</xs:complexType>

Если вам нужен расширенный тип, вы можете расширить выбор:

<xs:complexType name="abcType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:group ref="common_ab_elements"/>
        <xs:element name="c"/>
    </xs:choice>
</xs:complexType>

Это эквивалентно:

<xs:complexType name="abcType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:choice>
            <xs:element name="a"/>
            <xs:element name="b"/>
        </xs:choice>
        <xs:element name="c"/>
    </xs:choice>
</xs:complexType>

И по характеру операции выбора это в свою очередь эквивалентно:

<xs:complexType name="abcType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="a"/>
        <xs:element name="b"/>
        <xs:element name="c"/>
    </xs:choice>
</xs:complexType>

... это то, что вы хотите.

Если у вас есть атрибуты, тогда вам также может понадобиться определить общий базовый класс, который вы можете расширить. С этой схемой только классы без производных классов должны иметь элементы, так как это наличие элементов на базовых элементах, которые заставляют последовательность.

То, что мы эффективно делаем здесь, - это определение наследования элементов выбора отдельно от самой иерархии элементов.

Ответ 3

Другой пример с подстановками.

XSD

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="abExtension"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="abExtension" type="abExtensionType"/>
    <xs:complexType name="abExtensionType">
        <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element name="a"/>
            <xs:element name="b"/>
        </xs:choice>
    </xs:complexType>
    <xs:element name="abcExtension" substitutionGroup="abExtension">
        <xs:complexType>
            <xs:complexContent>
                <xs:extension base="abExtensionType">
                    <xs:choice minOccurs="0" maxOccurs="unbounded">
                        <xs:element name="c"/>
                    </xs:choice>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
    </xs:element>
    <xs:element name="abcdExtension" substitutionGroup="abExtension">
        <xs:complexType>
            <xs:complexContent>
                <xs:extension base="abExtensionType">
                    <xs:choice minOccurs="0" maxOccurs="unbounded">
                        <xs:element name="c"/>
                        <xs:element name="d"/>
                    </xs:choice>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
    </xs:element>
</xs:schema>

Пример XML, подтверждающий это,

abcExtension.xml

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test.xsd">
    <abcExtension>
        <b></b>
        <a></a>
        <c></c>
    </abcExtension>
</root>

abcdExtension.xml

  <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test.xsd">
<abcdExtension>
        <a>text</a>
        <b>test</b>
        <d>text</d>
        <c>text</c>
    </abcdExtension>
</root>

abExtension.xml

     <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test.xsd">
<abExtension>
        <b></b>
        <a></a> 
    </abExtension>
</root>

Ответ 4

Если ваш фокус направлен на расширение, а не наследование типа, <choice> можно расширить, переопределив <group> следующим образом:

Файл "abc.xsd", содержащий базовую схему:

<schema 
    xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="any" 
    xmlns:n="any" 
    elementFormDefault="qualified">

    <group name="baseGroup">
        <choice>
            <element name="a"/>
            <element name="b"/>
            <element name="c"/>
        </choice>
    </group>

    <complexType name="choiceType">
        <sequence minOccurs="0" maxOccurs="unbounded">
            <group ref="n:baseGroup"/>
        </sequence>
    </complexType>

    <element name="test">
        <complexType>
            <sequence>
                <element name="sample" type="n:choiceType" maxOccurs="unbounded"/>
            </sequence>
        </complexType>
    </element>
</schema>

Файл "abcdef.xsd", расширяющий <choice>, определенный в базовой схеме:

<?xml version="1.0"?>
<schema 
    xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="any"
    xmlns:n="any" 
    elementFormDefault="qualified">

    <redefine schemaLocation="abc.xsd">
        <group name="baseGroup">
            <choice>
                <group ref="n:baseGroup"/>
                    <element name="d"/>
                    <element name="e"/>
        </choice>
            </group>
    </redefine>
</schema>

Это подтверждает следующий XML файл, например:

<?xml version="1.0" encoding="UTF-8"?>
<test 
    xmlns="any"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="any ../schemas/abcde.xsd">

    <sample>
        <a/>
        <c/>
        <b/>
        <a/>
    </sample>

    <sample>
        <c/>
</sample>

    <sample>
    </sample>

    <sample>
        <a/>
        <e/>
        <b/>
        <d/>
        <a/>
    </sample>
</test>