0
votes

I am trying to build an XSD for the XML below:

<?xml version="1.0" encoding="UTF-8"?>
<people xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="people.xsd">
    <student>
        <name>John</name>
        <study_year>4</study_year>
        <study_level>A+</study_level>
        <age>21</age>
    </student>
    <tutor>
        <name>Thomas</name>
        <salary>2300</salary>
        <age>45</age>
        <expertise>Math</expertise>
    </tutor>
    <student>
        <name>Mike</name>
        <study_level>B-</study_level>
        <age>20</age>
        <study_year>22</study_year>
    </student>
</people>

Currently I have this XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="people">
        <xs:complexType>
            <xs:sequence maxOccurs="1">
                <xs:choice maxOccurs="unbounded">
                    <xs:element maxOccurs="unbounded" minOccurs="1" name="student">
                        <xs:complexType>
                            <xs:sequence maxOccurs="1">
                                <xs:group ref="person"/>
                                <xs:element name="study_year"/>
                                <xs:element name="study_level"/>
                            </xs:sequence>
                        </xs:complexType>
                    </xs:element>
                    <xs:element maxOccurs="unbounded" minOccurs="1" name="tutor">
                        <xs:complexType>
                            <xs:sequence maxOccurs="1">
                                <xs:group ref="person"/>
                                <xs:element name="salary"/>
                                <xs:element name="expertise"/>
                            </xs:sequence>
                        </xs:complexType>
                    </xs:element>
                </xs:choice>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:group name="person">
        <xs:sequence>
            <xs:element name="name"/>
            <xs:element name="age"/>
        </xs:sequence>
    </xs:group>
</xs:schema>

This is just an abstraction of the XSD I am trying to make. I would like to allow any order of the elements within student and tutor. I also need the xs:group because I don't want redundancy.

I have tried to replace the xs:sequence with xs:all, but that isn't valid XSD.

Is it possible to do this with an XSD?

Any help is appreciated!

1

1 Answers

1
votes

Unfortunately, you can only have elements within all, according to the spec: http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/structures.html#element-all

I don't think there's a way to do what you want (factor out the common name and age elements, in any order and occurring exactly once).

For example, here's two ways to approximate it, that are not allowed:

But there are three different trade-offs you could make to approximate it:

(1). Explicitly enumerate the orderings of (e.g. a choice of sequences: name,age or age,name), and have a sequence of them. Because you only have two elements in each group, this doesn't explode (at least, in those in this snippet). And it's still just an approximation, not the any order you wanted (and doesn't validate the example xml you gave):

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">

    <xs:group name="person">
      <xs:choice>
        <xs:sequence>
          <xs:element name="name"/>
          <xs:element name="age"/>
        </xs:sequence>
        <xs:sequence>
          <xs:element name="age"/>
          <xs:element name="name"/>
        </xs:sequence>
      </xs:choice>
    </xs:group>

    <xs:element name="people">
        <xs:complexType>
            <xs:sequence maxOccurs="1">
                <xs:choice maxOccurs="unbounded">
                    <xs:element maxOccurs="unbounded" minOccurs="1" name="student">
                      <xs:complexType>
                        <xs:sequence>

                          <xs:group ref="person"/>
                          <xs:choice>
                            <xs:sequence>
                              <xs:element name="study_year"/>
                              <xs:element name="study_level"/>
                            </xs:sequence>
                            <xs:sequence>
                              <xs:element name="study_level"/>
                              <xs:element name="study_year"/>
                            </xs:sequence>
                          </xs:choice>

                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>  
                    <xs:element maxOccurs="unbounded" minOccurs="1" name="tutor">
                      <xs:complexType>
                        <xs:sequence>

                          <xs:group ref="person"/>
                          <xs:choice>
                            <xs:sequence>
                              <xs:element name="salary"/>
                              <xs:element name="expertise"/>
                            </xs:sequence>
                            <xs:sequence>
                              <xs:element name="expertise"/>
                              <xs:element name="salary"/>
                            </xs:sequence>
                          </xs:choice>

                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>  
                </xs:choice>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

(2). Alternatively, you could give up on requiring it exactly once, and use the old trick of allowing any number of a choice of them - then you can factor out part of the choice:

 <xs:choice maxOccurs="unbounded">
   <xs:group ref="person"/>
   <xs:element name="study_year"/>
   <xs:element name="study_level"/>
  </xs:choice>

 <xs:group name="person">
   <xs:choice>
     <xs:element name="name"/>
     <xs:element name="age"/>
   </xs:choice>
 </xs:group>

(3). So, it seems best to give up factoring, and settle for duplicating the name and age each time. (Which it sounds like you were already reluctantly leaning towards).

My Answer: "No." It's a sensible thing you want to do, but I don't think it's possible. I think you have to choose between factoring or ordering/occurring (i.e. duplicate name/age everywhere; or require an order; or not restrict occurance).

Although this isn't a definitive answer, hopefully by exploring the options, it gives you a sense of the terrain. Perhaps some of the cleverer folk here can suggest a workaround, allowing factoring, non-ordering and occurring exactly once (or give a definitive proof that no such workaround is possible)...?

PS: Before anyone edits this, I used (1). numbering instead of a proper enumeration because the SO formatter was getting confused by it and not picking up the xml code within the second point.