Re: [xsl] Elements that inherit children from other elements

Subject: Re: [xsl] Elements that inherit children from other elements
From: Michael Kay <mike@xxxxxxxxxxxx>
Date: Fri, 10 Dec 2010 10:49:41 +0000
On 10/12/2010 10:23, Andreas Knote wrote:

I am currently developing an XML Schema for a university course catalogue. To ease maintenance, i thought of defining "module"s that inherit some or all child-elements from another "module" (i call those "derivedModule"). Any element that is not present inside of a "derivedModule" should be copied from its
"baseModule" (of type "module", the one referred to by an XS:IDREF, not a new type of element).

Right now, I use XS:ID and XS:IDREF to refer to a "module", and a XSL-Template eventually combines the "derivedModule" with it's "baseModule". It checks every child-element inside of a referred "baseModule" for an occurence of the same element in "derivedModule" and then copies either first or latter (if found).

I am not happy with this solution, as it feels wrong to have part of the structural definition, the relation between a "derivedModule" and a "module" (or it's children) as part of the stylesheet.

Now I am wondering whether it is actually possible to define such relations within an XML Schema, or whether it is against the philosophy behind XML.

XSD can describe constraints (eg using key/keyref, or using assertions in XSD 1.1) but it can't define relationships in the sense of functions that your XSLT code can use for navigation. I often find that it's a good idea to define a function library associated with the vocabulary/schema where the functions are designed to navigate these relationships, hiding their detail from the rest of your code.

Michael Kay

I am thankful for any suggestions,



Definiton for a "Module":

<xs:complexType name="ModulType"> <!-- Modul -->
<xs:element maxOccurs="1" minOccurs="1" name="Name" type="xs:string"/> <!-- Full name -->
<xs:element maxOccurs="1" minOccurs="1" name="Modulgruppe" type="xs:string"/> <!-- Group for combination -->
<xs:element maxOccurs="1" minOccurs="1" name="Bemerkung" type="FormatierterTextType"/> <!-- Annotation -->
<xs:attribute name="id" type="xs:ID" use="required"/> <!-- ID -->

Definition for a "derivedModule":

<xs:complexType name="AbgeleitetesModulType"> <!-- referring Module-->
<xs:element name="Basismodul" maxOccurs="1" minOccurs="1"> <!-- ID of baseModule to refer to-->
<xs:extension base="xs:IDREF">
<xs:attribute name="wahl" type="xs:string"/>
<xs:element maxOccurs="1" minOccurs="0" name="Name" type="xs:string"/> <!--optional elements which overwrite those of the baseModule->
<xs:element maxOccurs="1" minOccurs="0" name="Modulgruppe" type="xs:string"/>
<xs:element maxOccurs="1" minOccurs="0" name="Bemerkung" type="FormatierterTextType"/>
<xs:attribute name="id" type="xs:ID" use="required"/> <!-- new ID -->

XSL-Template to combine those two for further processing:

<xsl:template match="m:abgeleitetesModul">
<xsl:attribute name="id"><xsl:value-of select="./@id" /></xsl:attribute>
<xsl:variable name="basisID" select="m:Basismodul/text()" />
<xsl:variable name="abgModul" select="." />
<xsl:for-each select="//m:Modulhandbuch/m:Studiengang/m:Modul[@id=$basisID]/*"> <!-- Look for elements in baseModul, then check for occurences in derivedModule-->
<xsl:variable name="name" select="./local-name()" />
<xsl:when test="$abgModul/*[name() = $name]">
<xsl:apply-templates select="$abgModul/*[local-name() = $name]" mode="Inhalte" /> <!-- copy element from derivedModule -->
<xsl:apply-templates select="." mode="Inhalte" /> <!-- else copy element from baseModule -->

Current Thread