Как расширить выбор 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>
Ответы
Ответ 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>