Web Development

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!
12 comments
asi lev
asi lev

Is this problem part of the W3C standard, or just part of XMLSpy interpretation/implementaiton? I couldn't find any references on W3C. Maybe the solution should be changing the way XMLSpy interprets the schema (Using an "easy customers protest" perhaps? :)), allowing two choices to be interpreted as one choice section in the inherited element? Maybe better, the W3C standard should make an attribute in XSD that specifies how to relate to inherited choice lists (as sequence of choices or as combined choice). same goes maybe with inherited sequences (which one comes first?) Another standard improvement solution may be that the xsd author must repeat all previous choices, therefore creating a new choice list, that must include all elements from base types. this will solve the problem of the sequence order as well! (I like this solution best). I tried to see how another editor relates this issue. I used eclipse xml editor. Could it be that eclipse xml editor does not support inheritance from complex type? i've only managed to do simple type inheritance there...

Tony Hopkinson
Tony Hopkinson

are bleeding. I've always been a big believer in just because you can, doesn't mean you should. Thankyou for more proof.... When the solution becomes more of a problem than the problem. You've gone wrong. AJH

frank
frank

Oh, thank you! Somebody who gets this problem. I started to use inheritance because my (generated) xsd schema was over 20000 lines, and started to crash my editor. But being unable to join two choices, I am back to the drawing board. I always seem to be running into those brick walls when using the more complex xml stuff. If anyone knows a way around this (no matter how ugly ;) I am game) Frank

Justin James
Justin James

Ed - Good piece! This kind of stuff highlights why most developers are mostly using XML for a verbose replacement for CSV with rectangular data sets, or a replacement for INI/conf files, not for the stuff that the other 99.99% of the XML spec allows. :) J.Ja

Ed Woychowsky
Ed Woychowsky

That my personal philosophy is "What Evil Shall I Do Today?" :) Seriously, as software becomes more complex there will be times when the only workable solutions are things that were considered "out there" just a few short years ago. Look at AJAX, when I started using it to solve problems there were no libraries available, so it was required to hand-code everything. Now AJAX, in one form or another, is everywhere. We need to keep our minds open to new ways of doing things...just not so much that our brains fall-out with a wet "splot." :)

Ed Woychowsky
Ed Woychowsky

By "join two choices" do you mean merge two schemas?

Ed Woychowsky
Ed Woychowsky

One of the schema?s that I?m working with is 21,897 lines and another is 21,834. To edit them I use Altova?s XMLSpy 2008, which doesn?t have a problem with editting both large documents at the same time. Have you looked-into using xsl:include? You can break your schemas into multiple pieces.

Tony Hopkinson
Tony Hopkinson

Another shining example of just because you can doesn't mean you should. I consider Ajax to be yet another inefficient over hyped Kludge, to cope with HTTP statelessness. When circumstances force you to kick the kiss principle into touch, and through great effort, sheer cunning, with a smidgeon of total insanity, get the wand waving and something out of the door, again.... The victory is at best pyrrhic. By the way what is new about Ajax?

Tony Hopkinson
Tony Hopkinson

or simply aclimatised. I'm never ever ever going to be a big fan of client side scripting. I know all the arguments for it, but every one assumes that I as a consumer want the funky web page. I don't, I want quick clean and secure. Course I'm a grumpy old reactionary fart.

Justin James
Justin James

I few years ago, I was in the anti-AJAX camp for precisely the reason that few people had the time to write good, well tested AJAX. Now, with the prevalence of various controls and libraries, I have softened my stance on it. My objections are no longer the huge, "gee, have fun writing that monstrosity" ones, they are now things like, "you still have poor accessibility" and "making a dozen small calls to the server takes as long as one big call to the server, thanks to the time needed to set up and tear down an HTTP connection" and cross-platform is still rough and search engine visibility. :) J.Ja

Tony Hopkinson
Tony Hopkinson

I used to be one, but I had it beaten out of me. I like XML, it does what it does very well. Sometimes you can get more out of it than you thought you could. Other times the amount that you have to put in to get the that extra bit out will be too expensive on a technical basis, even if it is the pragmatic choice choice for other reasons. AJAX wasn't designed because it was the best solution to the problem of deploying RIAs, it was the least horrible way of delivering client side richness over the web using the existing infrastructure. Too many people mesmerised by the cool glowing button for my liking.

Ed Woychowsky
Ed Woychowsky

Since it's no longer necessary to ?roll your own? function libraries everybody is doing it now. Come to think of it, it reminds me of the ?I used to wear black before it was cool,? comment. Lately I've been play with XML and it's spawn, there's a lot of wide-open development areas out there yet. Do you think that my old boss was right and that I'm just a mad scientist at heart?

Editor's Picks