Search This Blog

Tuesday 30 July 2013

Order and Occurence indicators in xsd

Unlike simple types, complex types can have nested elements. Consider the below complex type:
<element name="address">
   <complexType>
      <element name="city" type="string" minOccurs="0"
         maxOccurs="1" default="-" />
      <element name="zipcode" type="string" nillable="false"
         minOccurs="1" maxOccurs="1" />
      <element name="country" type="string" fixed="India" />
   </complexType>
</element>
The above type is an invalid XML definition. We cannot nest elements directly within a complex type. They have to be placed within Order indicators.
Order Indicators are of three types:
  • Sequence 
  • All 
  • Choice
The first is the sequence indicator.
The sequence indicator specifies that the child elements must appear in a specific order.
<xs:complexType name="seqType">
   <xs:sequence>
      <xs:element name="value1" type="xs:string" minOccurs="0"
         maxOccurs="unbounded" />
      <xs:element name="value2" type="xs:string" minOccurs="0"
         maxOccurs="1" />
      <xs:element name="value3" type="xs:string" minOccurs="2"
         maxOccurs="4" />
      <xs:element name="value4" type="xs:string" minOccurs="1"
         maxOccurs="1" />
   </xs:sequence>
</xs:complexType>
The definition for a valid element will be
  • Any number of value1 elements (minimum 0) 
  • followed by at most 1 element of type value2 
  • followed by (at most 4 and atleast 2) elements of type value3 
  • followed by exactly 1 element of type value4. 
To validate such random number of elements it is required that the elements occur in a strict sequence. Then the validator used can verify the count rules specified. Hence the sequence element.
If we were to convert the above definition to java the class is :
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "seqType", propOrder = {
      "value1",
      "value2",
      "value3",
      "value4"
})
public class SeqType {

   protected List<String> value1;
   protected String value2;
   @XmlElement(required = true)
   protected List<String> value3;
   @XmlElement(required = true)
   protected String value4;

   // setter getters
}
A correct sequence would be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:seq xmlns:ns2="http://test.com/xsd/order/01/">
   <value1>1</value1>
   <value2>1</value2>
   <value3>1</value3>
   <value3>2</value3>
   <value4>1</value4>
</ns2:seq>
where seq is an element of type SeqType
<xs:element name="seq" type="o:seqType" />
If we did not provide the correct format - for e.g. I provided only 1 element of type value3 than the marshaling failed:
Exception in thread "main" javax.xml.bind.MarshalException
- with linked exception:
[org.xml.sax.SAXParseException: cvc-complex-type.2.4.b: The content of element 
'ns2:seq' is not complete. One of '{value3}' is expected.]
Similar attempts at unmarshaling an invalid XML resulted in exception:
Exception in thread "main" javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found 
starting with element 'value4'. One of '{value3}' is expected.]
Next up is the all indicator.
The all indicator specifies that the child elements can appear in any order, and that each child element must occur maximum once.
<xs:complexType name="allType">
   <xs:all>
      <xs:element name="value1" type="xs:string" minOccurs="0" />
      <xs:element name="value2" type="xs:string" minOccurs="0" />
      <xs:element name="value3" type="xs:string" />
      <xs:element name="value4" type="xs:string" />
   </xs:all>
</xs:complexType>
The rules for this type will be
  • Any sequence but value 3 and value 4 have to be there once 
  • while value 1 and value 2 may be present but at most once. 
Nobody cares of the sequence here as there is a limit on the number of occurrences of each element.The class generated for the same is as below:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "allType", propOrder = {})
public class AllType {

    protected String value1;
    protected String value2;
    @XmlElement(required = true)
    protected String value3;
    @XmlElement(required = true)
    protected String value4;
//setter getters
}
As expected there are no list elements here. value3 and value4 properties are a must. If we observe the annotations, then the propOrder property of XmlType has been left blank too.
The xml for the above xsd would be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:all xmlns:ns2="http://test.com/xsd/order/01/">
   <value1>1</value1>
   <value2>2</value2>
   <value3>3</value3>
   <value4>4</value4>
</ns2:all>
The last is the choice indicator.
It specifies that either one child element or another can occur.
<xs:complexType name="choiceType">
   <xs:choice>
      <xs:element name="value1" type="xs:string" minOccurs="0"
         maxOccurs="unbounded" />
      <xs:element name="value2" type="xs:string" minOccurs="0"
         maxOccurs="1" />
      <xs:element name="value3" type="xs:string" minOccurs="2"
         maxOccurs="4" />
      <xs:element name="value4" type="xs:string" />
   </xs:choice>
</xs:complexType>
Only one of these child elements can be there in the result.
The class created by XJC is as below:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "choiceType", propOrder = {
    "value1",
    "value2",
    "value3",
    "value4"
})
public class ChoiceType {

    protected List<String> value1;
    protected String value2;
    protected List<String> value3;
    protected String value4;

//setter getters
}
One possible xml for the element would be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:choice xmlns:ns2="http://test.com/xsd/order/01/">
   <value3>1</value3>
   <value3>2</value3>
</ns2:choice>
In the above XML fragment, only elements of type value3 will be allowed. If I tried to provide another child element than the marshaling would immediately raise a validation error:
Exception in thread "main" javax.xml.bind.MarshalException
- with linked exception:
[org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found 
starting with element 'value4'. One of '{value3}' is expected.]
This covers the varied order indicators in xsd.
In this post we have also used another type of indicators - Occurrence indicators.
Occurrence indicators as the name suggests, indicates the number of occurrences of an element in the xsd. They are represented as attributes
<xs:element name="names" type="xs:string" minOccurs="0" maxOccurs="1"/>
Here name can occur at most once only.
The default value of both these attributes is one. (This is also the case when talking about the all order indicator)
If we do not know the maximum number of names possible,  than we can leave the upper bound open.
<xs:element name="names" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
When the occurrence is greater than 1, XJC will represent the element a a List.

No comments:

Post a Comment