Projecting Objects to XML
Advanced Options for XML Schemas
[Home] [Back] [Next]
InterSystems: The power behind what matters   
Class Reference   
Search:    

This chapter discusses advanced options for creating XML schemas. It discusses the following topics:

The XML examples in this chapter are in literal format.
Class and Property Parameters Discussed in This Chapter
Automatic Creation of Types for Subclasses
When you define the XML projection for a class, all its subclasses are automatically mapped to separate types, all of which use the superclass as the base type. This means that wherever the supertype is used, you could instead use one of the subtypes. You can also use the subtypes to define either choice lists or substitution groups in the XML schema.
Note that you can define the XML projection for an abstract class; the class appears as the base type in any derived class schema even though, being abstract, it cannot be instantiated.
Consider an example. We start with a simple Person class:
Class UsingSubclasses.Person Extends (%Persistent, %XML.Adaptor)
{
Property Name As %String [Required];
Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h") [Required];
}
Suppose that we have two classes that are based directly on the Person class. First is the Patient class:
Class UsingSubclasses.Patient Extends UsingSubclasses.Person
{
Property PatientID As %String [Required];
}
Next is the Employee class:
Class UsingSubclasses.Employee Extends UsingSubclasses.Person
{
Property EmployeeID As %String [Required];
}
Finally, consider a class that uses Person as a property:
Class UsingSubclasses.Example1 Extends (%Persistent, %XML.Adaptor)
{
Property Person As UsingSubclasses.Person;
}
When you generate the schema for the Example1 class, the result is as follows:
<s:complexType name="Example1">
    <s:sequence>
        <s:element name="Person" type="Person" minOccurs="0" />
    </s:sequence>
</s:complexType>
<s:complexType name="Employee">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="EmployeeID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
<s:complexType name="Person">
    <s:sequence>
        <s:element name="Name" type="s:string" />
        <s:element name="DOB" type="s:date" />
    </s:sequence>
</s:complexType>
<s:complexType name="Patient">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="PatientID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
Notice the following:
Creating a Choice List of Subtypes
According to the XML Schema specification, a complex type can consist of a choice list of types, particularly related types. Suppose that instead of a <Person> element, we want the schema to permit a <Person>, <Patient>, or <Employee> element. To define such a schema, we would set the XMLTYPECONSTRAINT property parameter equal to "CHOICE" for the Person property, as follows:
Class UsingSubclasses.Example2 Extends (%Persistent, %XML.Adaptor)
{
Property Person As UsingSubclasses.Person(XMLTYPECONSTRAINT = "CHOICE");
}
By default, the choice list consists of all subclasses of the Person class. The schema for Example2 is as follows:
<s:complexType name="Example2">
    <s:sequence>
        <s:choice minOccurs="0">
            <s:element name="Employee" type="Employee" />
            <s:element name="Patient" type="Patient" />
            <s:element name="Person" type="Person" />
        </s:choice>
    </s:sequence>
</s:complexType>
<s:complexType name="Employee">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="EmployeeID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
<s:complexType name="Person">
    <s:sequence>
        <s:element name="Name" type="s:string" />
        <s:element name="DOB" type="s:date" />
    </s:sequence>
</s:complexType>
<s:complexType name="Patient">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="PatientID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
In contrast to the previous example, the type for Example2 is a choice list that consists of Person, Patient, or Employee. The latter three types are defined in the same way as in the previous example.
Restricting a Subclass from the Choice List
You might not want all subclasses to be included in the choice list. You can restrict the list of subclasses in two different ways.
The following sections show examples.
Example for Choice List With Explicit List
Suppose that we set the XMLCHOICELIST property parameter equal to a comma-separated list of the subclasses that we want to include in the choice list; for example:
Class UsingSubclasses.Example2A Extends (%Persistent, %XML.Adaptor)
{
Property Person As UsingSubclasses.Person
(XMLCHOICELIST = "UsingSubclasses.Patient, UsingSubclasses.Employee",
XMLTYPECONSTRAINT = "CHOICE");
}
The schema for this class would be as follows:
<s:complexType name="Example2A">
    <s:sequence>
        <s:choice minOccurs="0">
            <s:element name="Patient" type="Patient" />
            <s:element name="Employee" type="Employee" />
        </s:choice>
    </s:sequence>
</s:complexType>
<s:complexType name="Patient">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="PatientID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
<s:complexType name="Person">
    <s:sequence>
        <s:element name="Name" type="s:string" />
        <s:element name="DOB" type="s:date" />
    </s:sequence>
</s:complexType>
<s:complexType name="Employee">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="EmployeeID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
Notice that all four classes are represented (Example2A, Patient, Person, and Employee), but the choice list does not include the Person class. Also notice that the Person, Patient, and Employee types are defined in the same way as in the default case.
Example for Choice List with XMLINCLUDEINGROUP=0
Suppose that we add another subclass of the class Person and that we restrict it by setting XMLINCLUDEINGROUP to 0:
Class UsingSubclasses.Other Extends UsingSubclasses.Person
{
Parameter XMLINCLUDEINGROUP = 0;
}
In this case, this class is not included in the choice list and it is not included in the schema.
Creating a Substitution Group of Subtypes
The XML Schema specification also permits you to define a substitution group, which can be an alternative way to create choices. The syntax is somewhat different. Instead of making an explicit, central list of the types, you annotate the possible substitutes, as follows:
<s:complexType name="Example3">
    <s:sequence>
        <s:element ref="Person" minOccurs="0" />
    </s:sequence>
</s:complexType>
<s:element name="Person" type="Person"/>
<s:element name="Employee" type="Employee" substitutionGroup="Person"/>
<s:complexType name="Employee">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="EmployeeID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
<s:complexType name="Person">
    <s:sequence>
        <s:element name="Name" type="s:string" />
        <s:element name="DOB" type="s:date" />
    </s:sequence>
</s:complexType>
<s:element name="Patient" type="Patient" substitutionGroup="Person"/>
<s:complexType name="Patient">
    <s:complexContent>
        <s:extension base="Person">
            <s:sequence>
                <s:element name="PatientID" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
To create this schema, we use the following class:
Class UsingSubclasses.Example3 Extends (%Persistent, %XML.Adaptor)
{
Property Person As UsingSubclasses.Person
(XMLTYPECONSTRAINT = "SUBSTITUTIONGROUP");
}
Restricting a Subclass from the Substitution Group
For a given property, if you set the XMLTYPECONSTRAINT property parameter equal to "SUBSTITUTIONGROUP", the group automatically consists of all subclasses of the type of the property, as shown in the previous example. You can use the XMLINCLUDEINGROUP parameter to mark a given subclass so that it is not included in the substitution group. For example, suppose we add another subclass of the class Person:
Class UsingSubclasses.Other Extends UsingSubclasses.Person
{

Parameter XMLINCLUDEINGROUP = 0;

Property OtherID As %String [ Required ];
}
In this case, this class is not included in the substitution group. And because you have explicitly marked this class in this way, it is not included in the schema at all.
How Superclasses Are Represented as Types
If you need the XML schema to show a certain hierarchy of types, you need to understand how the projection interprets your Caché class hierarchy.
Your class hierarchy represents a meaningful organization of data, among other things. This hierarchy is mirrored, as much as possible, in the corresponding XML type definitions.
For example, suppose that you have the following classes:
What should the schema for Addition2 contain? It must represent all five properties. Also, because these classes are all user-defined, the schema for Addition2 should show the details of the class hierarchy; in contrast, if the Base extends a class from the Caché class library, which in turn extends other classes from that library, those details are less interesting.
Correspondingly, the XML schema for Addition2 looks as follows by default:
<s:complexType name="Addition2">
    <s:complexContent>
        <s:extension base="Addition1">
            <s:sequence>
                <s:element name="Addition2" type="s:decimal" minOccurs="0" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
<s:complexType name="Addition1">
    <s:complexContent>
        <s:extension base="Base">
            <s:sequence>
                <s:element name="Addition1" type="s:string" minOccurs="0" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
<s:complexType name="Base">
    <s:sequence>
        <s:element name="Property1" type="s:string" minOccurs="0" />
        <s:element name="Property2" type="s:decimal" minOccurs="0" />
        <s:element name="Property3" type="s:date" minOccurs="0" />
    </s:sequence>
</s:complexType>
Because XML type definitions do not support multiple inheritance, the Caché XML support makes certain simplifying assumptions. For a class that extends multiple superclasses, the type for the class is assumed to be the first listed superclass. An example is shown below. Consider the following three class definitions. AddressPart1 contains one property:
Class GXML.Writer.ShowMultiple.AddressPart1 Extends %XML.Adaptor
{
Property Street As %String [ Required ];
}
The AddressPart2 class contains another property:
Class GXML.Writer.ShowMultiple.AddressPart2 Extends %XML.Adaptor
{
Property City As %String [ Required ];
}
Finally, Address inherits from both of these classes (with AddressPart1 as the first superclass) and adds more properties:
Class GXML.Writer.ShowMultiple.Address Extends 
(GXML.Writers.ShowMultiple.AddressPart1, 
GXML.Writers.ShowMultiple.AddressPart2)
{
Property State As %String(MAXLEN = 2, PATTERN = "2u") [ Required ];
Property Zip As %String(MAXLEN = 10, PATTERN = "5n.1(1""-""4n)") [ Required ];
}
The XML schema for Address is as follows:
<s:complexType name="Address">
    <s:complexContent>
        <s:extension base="AddressPart1">
            <s:sequence>
                <s:element name="City" type="s:string" />
                <s:element name="State" type="s:string" />
                <s:element name="Zip" type="s:string" />
            </s:sequence>
        </s:extension>
    </s:complexContent>
</s:complexType>
<s:complexType name="AddressPart1">
    <s:sequence>
        <s:element name="Street" type="s:string" />
    </s:sequence>
</s:complexType>
Notice the following:
The following rules govern how the superclasses are handled when you view the schema for a given class:
Classes Based on Multiple XML-Enabled Superclasses
In some cases, a given class might be based on multiple XML-enabled superclasses. In such cases, the corresponding XML schema considers the order in which these classes are listed. For example, consider the following class, which inherits from two XML-enabled superclasses:
Class Test.Default Extends (Test.Superclass1, Test.Superclass2)
{
///additional class members ...
}
The XML schema for this class lists the XML types derived from the left-most class Test.Superclass1 before the XML types derived from Test.Superclass2. That same order occurs when you generate XML output for objects of this class.
If you instead want the XML schema (and the output) to be determined from right to left, specify the XMLINHERITANCE parameter as "right". For example:
Class Test.Default Extends (Test.Superclass1, Test.Superclass2)
{
Parameter XMLINHERITANCE = "right";

///additional class members ...
}