xmlxsdschemaprocessormultiple-choice

How to design an xsd for multiple choice questions?


Right now I have a system that allows users to create multiple choice questions. A user (idealy) would use them like this:

<MultipleChoice>
   <Choices>
      <Choice>a) apple</Choice>
      <Choice>b) banana</Choice>
      <Choice correct="true">c) grapes</Choice>
   </Choices>
</MultipleChoice>

Notice the correct attribute on grapes. That is used so that when I render it in the UI there is a green check mark next to it. However, with my current xsd I can't prevent the user for not putting any correct="true" attribute at all or putting it for multiple choices.

Examples of what I don't want users to be able to do are the following:

No correct choice (don't let users do this)

<MultipleChoice>
   <Choices>
      <Choice>a) apple</Choice>
      <Choice>b) banana</Choice>
      <Choice>c) grapes</Choice>
   </Choices>
</MultipleChoice>

Multiple correct choices (don't let users do this)

<MultipleChoice>
   <Choices>
      <Choice>a) apple</Choice>
      <Choice correct="true">b) banana</Choice>
      <Choice correct="true">c) grapes</Choice>
   </Choices>
</MultipleChoice>

How can I restrict users of my schema to be able to define one and only one correct choice? What I mean is I should be able to give them an error if they have violated the one and only one correct answer rule.

Here is my current schemas

...
<xs:complexType name="MultipleChoice">
   <xs:complexContent>
       <xs:element name="Choices" type="Choices" minOccurs="1" maxOccurs="1"/>
   </xs:complexContent>
</xs:complexType>

<xs:complexType name="Choices">
   <xs:sequence>
       <xs:element name="Choice" type="Choice" maxOccurs="unbounded" minOccurs="1"/>
   </xs:sequence>
</xs:complexType>

<xs:complexType name="Choice">
   <xs:simpleContent>
       <xs:extension base="xs:string">
           <xs:attribute name="correct" type="xs:boolean" default="false"/>
       </xs:extension>
   </xs:simpleContent>
</xs:complexType>

Solution

  • Easiest way would be to define it as a seperate element and restrict it's use to only one. So my suggestion would be to define it like this:

    <xs:complexType name="MultipleChoice">
       <xs:complexContent>
           <xs:element name="Choices" type="Choices" minOccurs="1" maxOccurs="1"/>
       </xs:complexContent>
    </xs:complexType>
    
    <xs:complexType name="Choices">
       <xs:sequence>
           <xs:element name="Choice" type="Choice" maxOccurs="3" minOccurs="1"/>
           <xs:element name="CorrectChoice" type="CorrectChoice" maxOccurs="1" minOccurs="1"/>
       </xs:sequence>
    </xs:complexType>
    
    <xs:complexType name="Choice">
       <xs:simpleContent>
           <xs:extension base="xs:string"/>
       </xs:simpleContent>
    </xs:complexType>
    
    <xs:complexType name="CorrectChoice">
       <xs:simpleContent>
           <xs:extension base="xs:string"/>
               <xs:attribute name="correct" type="xs:boolean" default="false" use="required"/>
           </xs:extension>
       </xs:simpleContent>
    </xs:complexType>
    

    Otherwise if you are using XML 1.1 and want to keep the element Choice by itself then you could solve it by using an assert that checks and counts how often you use the attribute correct. Something like this should work:

    <xs:assert test="count(Choices//@correct) le 1"/>
    

    This assert will limit the number of attributes correct that can be applied within one element Choices to 1. So any element Choices with more then one Choice that has the attribute correct will fail.