AN XML CASE STUDY

EXAMPLE SCHEMA

  
Rules: 

  • Root element is <quiz>.
  • <quiz> contains a <title> and an <items> element.
  • <items> contains one or more <item> element.
  • <item> contains one <question>, at least 2 <answer>s, and no more than 6 <answer>s.
  • One of the answer must have an attribute "correct=y" which indicates the correct answer.
  • <question> might appear before of after all the <answer>s.
<?xml version="1.0"?>
<quiz>
  <title>The Example Quiz</title>
  <items>
    <item>
      <question>In which continent is the country Japan located?</question>
      <answer correct="y">Asia</answer>
      <answer>Europe</answer>
      <answer>Africa</answer>
      <answer>America</answer>
    </item>
    <item>
      <answer>Tuna</answer>
      <answer correct="y">Cow</answer>
      <answer>Whale</answer>
      <answer>Lobster</answer>
      <question>Which one cannot swim?</question>
    </item>		
    <item>
      <question>How many points are on a hexagon?</question>
      <answer>5</answer>
      <answer>6</answer>
      <answer>7</answer>
      <answer correct="y">8</answer>
    </item>		
  </items>
</quiz>
  • A DTD declaration for that XML spec: 
    <!ELEMENT quiz (title, items)>
    <!ELEMENT title (#PCDATA)>
    <!ELEMENT items (item)+> 
    <!ELEMENT item ((question, answer, answer+)|(answer, answer+, question))>
    <!ELEMENT question (#PCDATA)>
    <!ELEMENT answer (#PCDATA)>
    <!ATTLIST answer correct (y) #IMPLIED>

XML with DTD

Problems:

  • Hard to limit the number of <answer>s to 6 maximum.  One way will be to declare it like:
    <!ELEMENT item (question, ((answer, answer)|
    (answer, answer, answer)|
    (answer, answer, answer, answer)|
    (answer, answer, answer, answer, answer)|
    (answer, answer, answer, answer,answer,answer)))>
    but do you want to?  
    Even with that, you still have to handle the requirement where <question> might appear after the <answer>s, so it will be:
    <!ELEMENT item ((question, ((answer, answer)|
    (answer, answer, answer)|
    (answer, answer, answer, answer)|
    (answer, answer, answer, answer, answer)|
    (answer, answer, answer, answer,answer,answer)))|
    (((answer, answer)|
    (answer, answer, answer)|
    (answer, answer, answer, answer)|
    (answer, answer, answer, answer, answer)|
    (answer, answer, answer, answer, answer, answer)), question))>
  • The DTD does not limit the number of <answer>s that has the "correct" attribute.  So there might be an <item> with 2 or more correct answers.  Can this be solved without changing the structure of the XML?  Probably not.  
  • Create a Schema declaration for that XML spec:
    Attempt 1:
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <xsd:element name="title" type="xsd:string"/>
  <xsd:element name="question" type="xsd:string"/>
    
  <xsd:element name="answer">
    <xsd:complexType>
      <xsd:simpleContent>
      <xsd:extension base="xsd:string">
        <xsd:attribute name="correct" type="xsd:string"/>
      </xsd:extension>    
      </xsd:simpleContent>
    </xsd:complexType>  
  </xsd:element>  

  <xsd:complexType name="itemType">
    <xsd:sequence>
      <xsd:element ref="question"/>
      <xsd:element ref="answer" minOccurs="2" maxOccurs="6"/>  
    </xsd:sequence>  
  </xsd:complexType>

  <xsd:element name="item" type="itemType"/>

  <xsd:complexType name="itemsType">
    <xsd:sequence>
      <xsd:element ref="item"  minOccurs="1" maxOccurs="unbounded"/>
    </xsd:sequence>    
  </xsd:complexType>      
  
  <xsd:element name="items" type="itemsType"/>
  
  <xsd:element name="quiz">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="title" minOccurs="1" maxOccurs="1"/>
        <xsd:element ref="items" minOccurs="1" maxOccurs="1"/>
      </xsd:sequence>
    </xsd:complexType>    
  </xsd:element>  
</xsd:schema>

Problems:

  • <question> must appear before <answer>s in this schema because it's declared in <xsd:sequence>.
  • The <answer> attribute "correct" can be assigned any values, while we only want to accept "y".
  • This schema allows more than 1 correct <answer>s.  Again, it might not be possible to create a schema which prevents this.
Attempt 2:
  
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <xsd:element name="title" type="xsd:string"/>
  <xsd:element name="question" type="xsd:string"/>
    
  <xsd:simpleType name="correctType">
     <xsd:restriction base="xsd:string">
  	<xsd:enumeration value="y"/>
    </xsd:restriction>
  </xsd:simpleType>    
  
  <xsd:element name="answer">
    <xsd:complexType>
      <xsd:simpleContent>
      <xsd:extension base="xsd:string">
        <xsd:attribute name="correct" type="correctType"/>
      </xsd:extension>    
      </xsd:simpleContent>
    </xsd:complexType>  
  </xsd:element>  

  <xsd:element name="item">
    <xsd:complexType>
      <xsd:choice>
        <xsd:sequence>
          <xsd:element ref="question"/>
          <xsd:element ref="answer" minOccurs="2" maxOccurs="6"/>
        </xsd:sequence>  
        <xsd:sequence>
          <xsd:element ref="answer" minOccurs="2" maxOccurs="6"/>    
          <xsd:element ref="question"/>
        </xsd:sequence>      
      </xsd:choice>
    </xsd:complexType>
  </xsd:element>
  
  <xsd:element name="items">
    <xsd:complexType>  
      <xsd:sequence>
        <xsd:element ref="item"  minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence> 
    </xsd:complexType>         
  </xsd:element>  
  
  <xsd:element name="quiz">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="title" minOccurs="1" maxOccurs="1"/>
        <xsd:element ref="items" minOccurs="1" maxOccurs="1"/>
      </xsd:sequence>
    </xsd:complexType>    
  </xsd:element>  
</xsd:schema>
  
This schema is much cleaner that before.  We used a lot of unnamed types because we don't need to reuse the types.  "correctType" is a type that contains only 1 valid value, which is "y".  The <item> element is declared within <xsd:choice> to allow <question> to appear before or after the <answer>s.

Problems:

  • This schema allows more than 1 correct <answer>s.  

DISPLAYING THE XML DATA

  
Displaying data using CSS style sheet.  

  • Style sheet example 1
    title 
    {
    font-family:helvetica;font-size: 30px;display:block;color:red;
    }
    
    items
    {
    display: block;font-family:helvetica;font-size: 12px;color: black;
    }
    
    item
    {
    display: block;border:1px solid gray;margin: 3mm;font-family:helvetica;
    font-size: 12px;color: black;
    }
    
    question
    {
    display: block;font-family:helvetica;font-size: 16px;
    font-weight: bold;color: blue;
    } 
    
    answer
    {
    display: block;font-family:helvetica;font-size: 12px;color: black;
    } 

    Result on IE6:



  • Style sheet example 2
    title 
    {
    	font-family:helvetica;
    	font-size: 30px;	
    	display:block;
    	color:white;
    	background:rgb(0,0,0);
    }
    
    items
    {
    	bacgkround:#DDDDDD;
    	display: block;
    	font-family:helvetica;
    	font-size: 12px;
    	color: black;
    }
    
    item
    {
    	display: block;
    	margin: 0mm;
    	font-family:helvetica;
    	font-size: 12px;
    	color: black;
    }
    
    question
    {
    	display: block;
    	font-family:helvetica;
    	font-size: 16px;
    	font-weight: bold;
    	color: blue;
    	position: relative;
    	background:#DDDDDD;
    	border: solid 1px;
    }	
    
    answer
    {
    	display: block;
    	position:relative;
    	left:50px;
    	font-family:helvetica;
    	font-size: 12px;
    	color: black;
    	background:#DDDD88;	
    }	

    Result on IE6:

      

  • Style sheet 3 (show questions only)
    The answer element style is set to visibility: hidden.
    title 
    {
    	font-family:helvetica;
    	font-size: 30px;	
    	display:block;
    	color:white;
    	background:rgb(0,0,0);
    }
    
    item
    {
    	display: block;
    	font-family:helvetica;
    	font-size: 12px;
    	color: black;
    }
    
    question
    {
    	display: block;
    	font-family:helvetica;
    	font-size: 16px;
    	color: black;
    }	
    
    answer
    {
    	visibility: hidden;
    }	

    Result on IE6:

Displaying Data Using XSLT.  

  • Hides answers: example 1
    <?xml version="1.0"?>
    <quiz:stylesheet 
      xmlns:quiz="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
      <quiz:template match="/">
        <html>
          <head><title>Quiz Example formatted with XSLT</title></head>
          <body>
    	<quiz:apply-templates />
          </body>
        </html>
      </quiz:template>
    
      <quiz:template match="title">
        <H1><quiz:value-of select="."/></H1>
      </quiz:template>
    
      <quiz:template match="answer">
      </quiz:template>
    
      <quiz:template match="question">
        <b><quiz:value-of select="."/></b><br/>	
      </quiz:template>
    </quiz:stylesheet>	

    What that does:

    • <title> is formatted with <H1>.
    • <question> is formatted with <B>. 
    • <answer> is not printed.
    • <BR> is appended after every question.
    • All other elements are ignored:

      Result on IE6:

        
  • Add bullet on every answer:: example 2
    <?xml version="1.0"?>
    <quiz:stylesheet 
      xmlns:quiz="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
      <quiz:template match="/">
        <html>
          <head><title>Quiz Example formatted with XSLT</title></head>
          <body>
    	<table border="1" bgcolor="#EEEEEE">			
    	<quiz:apply-templates/>
    	</table>
          </body>
        </html>
      </quiz:template>
    
      <quiz:template match="title">
        <tr><td><H1><quiz:value-of select="."/></H1></td></tr>
      </quiz:template>
    
      <!-- each item is put inside a cell table, --> 
      <!-- an "item" is numbered using ordered list OL-->
      <quiz:template match="items">
        <tr><td><ol><quiz:apply-templates/></ol></td></tr>
      </quiz:template>
    
      <quiz:template match="item">
        <LI><b><font size="4" color="0000FF">
           <quiz:value-of select="question"/></font></b></LI><br/>	
        <!-- loop answers and apply bullet for each answer-->
        <UL>	
          <quiz:for-each select="answer">
            <LI><quiz:value-of select="."/></LI>
          </quiz:for-each>
        </UL>	
      </quiz:template>
    </quiz:stylesheet>

    What that does:

    • Creates a table within the body of the html.
    • <title> is printed inside a table cell as <H1>.
    • To put a number for each item, <items> mark a beginning of an ordered list <OL>.
    • A <question> is a list item <LI> of the ordered list <OL>.  This causes the number to be printed.
    • Each answer is marked as a list item <LI> of a nested unordered list <UL>, this produces the bullets.

    Result on IE6:

      

  • Show correct answer in different color: example 3
    <?xml version="1.0"?>
    <quiz:stylesheet 
      xmlns:quiz="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
      <quiz:template match="/">
        <html>
          <head><title>Quiz Example formatted with XSLT</title></head>
          <body><table border="1" bgcolor="#EEEEEE">
            <quiz:apply-templates/></table>
          </body>
        </html>
      </quiz:template>
    
      <quiz:template match="title">
        <tr><td><H1><quiz:value-of select="."/></H1></td></tr>
      </quiz:template>
    
      <!-- is numbered using ordered list OL-->
      <quiz:template match="items">
        <tr><td><ol><quiz:apply-templates/></ol></td></tr>
      </quiz:template>
    
      <quiz:template match="item">
        <LI><b><font size="4" color="0000FF">
           <quiz:value-of select="question"/></font></b></LI><br/> 
    
        <!-- loop answers and apply bullet for each answer-->
        <UL> 
        <quiz:for-each select="answer">
          <!-- display correct answer in different style--> 
          <quiz:choose>
            <quiz:when test="@correct='y'">
               <font color="red"><LI><quiz:value-of select="."/></LI></font>
            </quiz:when>
            <!-- Comment out the otherwise below to just show the -->
            <!-- correct answer.-->
            <!-- below is a different way to apply  -->
            <!-- the template to the "answer" node-->
            <!-- It selects the node first, then applies the templaye -->
            <!-- the "when" above can also call the same apply template -->
            <!-- instead of writing out the code again-->
            <quiz:otherwise>
               <quiz:apply-templates select="." />
            </quiz:otherwise>
          </quiz:choose> 
        </quiz:for-each>
        </UL> 
      </quiz:template>
    
      <quiz:template match="answer">
      <li><quiz:value-of select="."/></li>
      </quiz:template>
    
    </quiz:stylesheet>

    This XSLT is almost identical to the one above, except this one check the attribute of the <answer> element and prints the <answer> with attribute correct="y" in different color.  It uses <choose> and <when> tests to check the attributes.

    Result on IE6:

      

  • Show correct answer only: example 4
    <?xml version="1.0"?>
    <quiz:stylesheet 
      xmlns:quiz="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
      <quiz:template match="/">
        <html>
        <head><title>Quiz Example formatted with XSLT</title></head>
        <font face="Arial,helvetica, verdana">
        <body>
          <table border="1" bgcolor="#EEEEEE">			
            <quiz:apply-templates/>
          </table>
        </body>
        </font>
        </html>
      </quiz:template>
    
      <quiz:template match="title">
        <tr><td><H1><quiz:value-of select="."/></H1></td></tr>
      </quiz:template>
    
      <quiz:template match="items">
        <quiz:apply-templates/>	
      </quiz:template>
    
      <quiz:template match="item">
        <b><font size="3" color="0088FF">
          <quiz:value-of select="question"/></font></b></LI>
        <!-- loop answers and apply bullet for each answer-->
        <TR><TD>
        <UL>	
          <quiz:for-each select="answer">
            <!-- display correct answer in different style-->			
            <quiz:choose>
              <quiz:when test="@correct='y'">
                <font color="black"><LI>
                  <quiz:value-of select="."/></LI>
                </font>
              </quiz:when>
            </quiz:choose>	
         </quiz:for-each>
        </UL>	
        </TD></TR>
      </quiz:template>
    </quiz:stylesheet>

This XSLT is almost identical to the one above, except this one ignores any <answer> that does not have the attribute correct="y"; and also, this one puts each <item> inside a table cell.

Result on IE6:

<?xml version="1.0"?>
<quiz:stylesheet 
  xmlns:quiz="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <quiz:template match="/">
    <html>
    <head><title>Quiz Example formatted with XSLT</title></head>
    <body><quiz:apply-templates/></body>
    </html>
  </quiz:template>

  <quiz:template match="title">
  </quiz:template>
  
  <quiz:template match="item">
      <quiz:for-each select="answer">
        <quiz:choose>
          <quiz:when test="@correct='y'">
            <P/><font color="black"><B>
               <quiz:value-of select="."/></B> is the correct answer for: 
               </font>
            <font color="red">
              <I><quiz:value-of select="../question"/></I>
            </font>
          </quiz:when>
        </quiz:choose>	
     </quiz:for-each>
  </quiz:template>
</quiz:stylesheet>

This XSLT ignores the <title> element.  It then selects and prints the correct answer.  Following that, it uses XPath to find the <question> and prints the question.

Result on IE6:

<<INDEX>>

(C) 2002 F. Permadi