Advanced Options for XML Schemas
This topic discusses advanced options for creating XML schemas.
The XML examples shown here are in literal format.
Class and Property Parameters Discussed on This Page |
---|
|
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:
-
The type for Example1 is Person.
-
The Employee type and the Patient type are both defined; this is the default. (For reference, this corresponds to setting the XMLTYPECONSTRAINT property parameter equal to "EXPLICIT".)
-
According to the XML Schema specification, the preceding schema means that wherever a <Person> element is included, you could include either an <Employee> element or a <Patient> element.
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.
-
You can use the XMLINCLUDEINGROUP class parameter to mark a given subclass so that it is not included in the choice list.
-
You can set the XMLCHOICELIST property parameter equal to a comma-separated list of the subclasses in the choice list.
-
This parameter takes precedence; that is, if a subclass is listed in XMLCHOICELIST, it is included in the choice list, even if the subclass is marked as XMLINCLUDEINGROUP = 0.
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 InterSystems IRIS® data platform 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:
-
A class named Base, which defines three public properties (Property1, Property2, and Property3).
-
A class named Addition1, which extends Baseand which defines an additional public property (Addition1).
-
A class named Addition2, which extends Addition1 and which defines an additional public property (Addition2).
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 InterSystems IRIS 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 InterSystems IRIS 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 first listed superclass, AddressPart1, is represented by the corresponding XML type, which contains all the expected details.
-
Apart from the property contained by the AddressPart1 type, all remaining properties are assigned to the Address type. This is the only possible representation of these properties, once the AddressPart1 class has been mapped.
-
Both AddressPart1 and AddressPart2 are subclasses of %XML.AdaptorOpens in a new tab, yet no structure of %XML.AdaptorOpens in a new tab class is exposed. The emphasis is on the custom classes, which is appropriate.
The following rules govern how the superclasses are handled when you view the schema for a given class:
-
If a superclass inherits from %XML.AdaptorOpens in a new tab, it is represented by an XML type, which represents all the projected properties of this class. The short class name is taken as the XML type for the property. If the class specifies a value for theXMLTYPE parameter, that value is used as the type name instead.
-
If a superclass does not inherit from %XML.AdaptorOpens in a new tab, it is not represented by an XML type. If it has any properties, they are assigned to the inheriting class (the class whose schema you are viewing).
-
If the given class inherits from multiple superclasses, an XML type is created for the first superclass (if applicable; see the preceding rules). All properties that do not belong to the first superclass are assigned to the inheriting class, as in the previous example.
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 ...
}