Developer

Create a complex type using XML schema inheritance

Using inheritance, it is possible to create a complex type and then create an element that extends it by adding elements or attributes. Edmond Woychowsky shows how he fudged inheritance using XSLT.

 

In the original Star Trek episode, "Amok Time," Spock utters the line, "After a time, you may find that having is not so pleasing a thing after all as wanting. It is not logical, but it is often true." Even though Spock was referring to T'Pring at the time, it is a concept that isn't limited to any one person or thing. If you don't believe me, might I suggest a double dip ice cream cone on a hot and humid July evening? It's not something you do more than once, at least not without a shower handy. You can apply the "wanting is better than having" concept to development work. An example is the limited form of inheritance available in XML schemas.

Taken at face value, schema inheritance is a good idea. Using inheritance, it is possible to create a complex type and then create an element that extends it by adding elements or attributes. Listing A shows an example of a schema that implements inheritance. Listing A
<?xml version="1.0" encoding="UTF-8"?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">

      <xs:complexType name="inner">

            <xs:choice minOccurs="0" maxOccurs="unbounded">

                  <xs:element name="a" type="xs:string"/>

                  <xs:element name="c" type="xs:string"/>

            </xs:choice>

      </xs:complexType>

      <xs:element name="root">

            <xs:complexType>

                  <xs:complexContent>

                        <xs:extension base="inner">

                              <xs:choice minOccurs="0" maxOccurs="unbounded">

                                    <xs:element name="b" type="xs:string"/>

                                    <xs:element name="d" type="xs:string"/>

                              </xs:choice>

                        </xs:extension>

                  </xs:complexContent>

            </xs:complexType>

      </xs:element>

</xs:schema>
The way that the schema works is that the xs:complexType, inner, specifies two elements named a and c whose order doesn't matter thanks to the attributes of the xs:choice. The xs:element, root, then inherits from inner and adds the elements b and d whose order also doesn't matter. The result of this inheritance is that the XML document in Listing B is perfectly valid. Listing B
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="G:Documentsxmlinheritancetestsample.xsd">      <a>one</a>

      <b>two</b>

</root>
So far, so good. Unfortunately, the not being as pleasurable as you want issues are about to rear their ugly head like a Mugato. These issues arise when validating an XML document like the one shown in Listing C. Listing C
<?xml version="1.0" encoding="UTF-8"?><root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="G:Documentsxmlinheritancetestsample.xsd">

      <a>one</a>

      <b>two</b>

      <c>three</c>

      <d>four</d>

</root>
The problem is that, in spite of xs:choice being used for both the base and the derived, the base and the derived are independent of each other. So, once an element from the derived element is encountered, any element from the base is invalid, like c in the above example. The actual error is shown in Altova's XMLSpy (see Figure 1). Figure 1

Figure 1

Click image to enlarge.

This issue leaves three possibilities. The first is to forget the whole idea, which in my case wasn't an option. The second possibility is to make sure that the elements are sequenced so that base elements all occur before the derived elements. As attractive as this possibility is, due to the fact that it isn't always possible to control the source of an XML document, it also isn't an option, alas. In the end, I decided to fudge inheritance using XSLT.

The way it works is that the XSL style sheet uses the schema as input to create a new schema. The first step is to create the xs:schema element along with its attributes. Next, it is necessary to select those xs:element elements that have xs:extension elements as descendants. At this point, the generic xs:complexType and xs:choice elements are created. Finally the xs:element elements are copied from both the derived and base complex types. In the end, the transformation looks like the one shown in Listing D, with the result of the transformation shown in Listing E. Listing D
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">

      <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

      <xsl:template match="/">

            <xsl:element name="xs:schema">

                  <xsl:attribute name="version">1.0</xsl:attribute>

                  <xsl:apply-templates select="//xs:element[count(./descendant::node()[name(.) = 'xs:extension']) != 0]"/>

            </xsl:element>

      </xsl:template>

      <xsl:template match="xs:element">

            <xsl:variable name="base" select="./descendant::node()[name(.) = 'xs:extension']/@base"/>

            <xsl:copy>

                  <xsl:copy-of select="./@*"/>

                  <xsl:element name="xs:complexType">

                        <xsl:element name="xs:choice">

                              <xsl:attribute name="minOccurs">0</xsl:attribute>

                              <xsl:attribute name="maxOccurs">unbounded</xsl:attribute>

                              <xsl:copy-of select="./descendant::node()[name(.) = 'xs:element']"/>

                              <xsl:copy-of select="//xs:complexType[@name = $base]/descendant::node()[name(.) = 'xs:element']"/>

                        </xsl:element>

                  </xsl:element>

            </xsl:copy>

      </xsl:template>

</xsl:stylesheet>
Listing E
<?xml version="1.0" encoding="UTF-8"?><xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

      <xs:element name="root">

            <xs:complexType>

                  <xs:choice minOccurs="0" maxOccurs="unbounded">

                        <xs:element name="b" type="xs:string"/>

                        <xs:element name="d" type="xs:string"/>

                        <xs:element name="a" type="xs:string"/>

                        <xs:element name="c" type="xs:string"/>

                  </xs:choice>

            </xs:complexType>

      </xs:element>

</xs:schema>

Conclusion

Some developers might take issue with the idea of what is described here as being true inheritance. On some level, I agree. Some might even consider the code in this article to be a sham, a con, or a fake. I, however, like to think of it as a Corbomite Maneuver. It does, after all, get the job done.

Get weekly development tips in your inbox Keep your developer skills sharp by signing up for TechRepublic's free Web Developer newsletter, delivered each Tuesday. Automatically subscribe today!

Editor's Picks