简体   繁体   中英

How do I write an XML Schema to allow/impose elements to occur one time in any order?

The XML has a parent element parent , which has to contain 10 child elements: child1 to child10 in any order, but exactly one time.

As an alternative, the parent element can optionally contain child11 to child20 instead of child1 to child10 .

<parent><child1/><child4/> ... <child2/></parent> OK
<parent><child15/>...<child20/></parent> OK
<parent><child1/><child2/></parent> BAD, missing childs
<parent><child1/>...<child11/></parent> BAD: child11 shall never be with child1
<parent><child1/><child1/></parent> BAD: childs shall not be repeated

This is my schema definition attempt so far:

<xs:complexType name="parent">
    <xs:choice>
        <xs:sequence>
            <xs:element name="child1" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            <xs:element name="child2" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            ...
        </xs:sequence>
        <xs:sequence>
            <xs:element name="child11" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            <xs:element name="child12" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            ...
        </xs:sequence>
    </xs:choice>
</xs:complexType>

XML Schema 1.0 doesn't have a direct solution to your problem.

What you'd want, is the following (replacing the xs:sequence with xs:all ), but this isn't valid XML Schema language :

<xs:complexType name="parent">
    <xs:choice>
        <xs:all>
            <xs:element name="child1" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            <xs:element name="child2" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            <!-- ... -->
        </xs:all>
        <xs:all>
            <xs:element name="child11" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            <xs:element name="child12" type="xs:integer" minOccurs="1" maxOccurs="1"/>
            <!-- ... -->
        </xs:all>
    </xs:choice>
</xs:complexType>

The problem is that xs:choice allows nesting of xs:sequence and xs:choice compositors, but not xs:all .

There are five possibilities I see:

  1. Use the schema in your question, which uses xs:sequence instead of xs:all , but use an XSL transformation to re-order as per the schema before validation.
  2. If XML Schema 1.1 is an option, you may be able to define a more lax schema (ie one that doesn't enforce all your rules, but accepts all valid input), but get the extra checking with xs:assert .
  3. Alternatively define a more lax schema, but do the extra checking in your own program, after the schema validation step.
  4. Restructure the schema so that the two groups of related elements are nested under another element, so that you can use xs:choice .
  5. Define parent as an abstract type, define derived types for the two choices of content models, and use xsi:type to specify which one is being used in the XML documents.

Both of the latter 2 options require changing the XML format unfortunately.


This is how your schema might look if you can add an extra level to the element nesting:

<xs:complexType name="option1">
    <xs:all>
        <xs:element name="child1" type="xs:integer" minOccurs="1" maxOccurs="1"/>
        <xs:element name="child2" type="xs:integer" minOccurs="1" maxOccurs="1"/>
        <!-- ... -->
    </xs:all>
</xs:complexType>

<xs:complexType name="option2">
    <xs:all>
        <xs:element name="child11" type="xs:integer" minOccurs="1" maxOccurs="1"/>
        <xs:element name="child12" type="xs:integer" minOccurs="1" maxOccurs="1"/>
        <!-- ... -->
    </xs:all>
</xs:complexType>

<xs:complexType name="parent">
    <xs:choice>
        <xs:element name="option1" type="option1"/>
        <xs:element name="option2" type="option2"/>
    </xs:choice>
</xs:complexType>

This is how your schema might look if you use type substitution:

<xs:complexType name="parent" abstract="true">
</xs:complexType>

<xs:complexType name="parent_option1">
    <xs:complexContent>
        <xs:extension base="parent">
            <xs:all>
                <xs:element name="child1" type="xs:integer" minOccurs="1" maxOccurs="1"/>
                <xs:element name="child2" type="xs:integer" minOccurs="1" maxOccurs="1"/>
                <!-- ... -->
            </xs:all>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>

<xs:complexType name="parent_option2">
    <xs:complexContent>
        <xs:extension base="parent">
            <xs:all>
                <xs:element name="child11" type="xs:integer" minOccurs="1" maxOccurs="1"/>
                <xs:element name="child12" type="xs:integer" minOccurs="1" maxOccurs="1"/>
                <!-- ... -->
            </xs:all>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>

<xs:element name="parent" type="parent"/>

An XML instance document that uses this approach would look like the following for your first group of elements (note the use of xsi:type to specify which group has been selected):

<parent
    xmlns="Your namespace here"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:type="parent_option1">

    <child2>2</child2>
    <child1>1</child1>
    <!-- ... -->

</parent>

And for the second group:

<parent
    xmlns="Your namespace here"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:type="parent_option2">

    <child11>11</child11>
    <child12>12</child12>
    <!-- ... -->

</parent>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM