[Home] [By Thread] [By Date] [Recent Entries]
Hi,
Some time ago I found a question on the notion of Inheritance from William Bagby on this mailing list. (http://www.biglist.com/lists/xsl-list/archives/200103/msg01509.html) I have also XML inheritance requirements. I used the basic inputs from Jeni and Michael and a lot of XSLT tinkering to come up with a far more general solution. I think that this style sheet is usable as a general purpose tool in many projects. The solution works well. The only problem I can't solve is how to get rid of the namespace declaration xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance" in the out.xml result file. I think there is no solution. Has anyone any suggestions? I would also appreciate any feedback, remarks or enhancements to this solution. Steve Van Hoyweghen ************************** Start of file transform.xsl ************************* <?xml version="1.0" encoding="utf-8"?> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - file : transform.xsl date : 29 Sep 2001 version : 1.0 author : Steve Van Hoyweghen use : An inheritance implementation for XML elements. remarks : This is only tested with the Microsoft MSXML3 XSLT processor. There is only one dependency on this processor which is clearly marked. It should be easy to adapt to another XSLT processor implementation. See comments for more information. This style sheet implements an inheritance mechanism for XML fragments of selected XML elements and their associated children, attributes and text nodes. A prototype-based single inheritance approach is chosen. Principle ~~~~~~~~~ Assume two XML fragments A and A'. The precondition is that the element name of both fragments are identical. Assume also that fragment A' inherits from fragment A. All elements, attributes and text nodes from the subtree modeled by fragment A' are copied without further processing. All elements, attributes and text nodes not present in fragment A', but with an occurrence in fragment A, are added to the resulting instantiation. The inheritance tree is recursively traveled from a leaf of the inheritance tree up to the root. This implementation supports inheritance chains of any length, e.g. A <- A' <- A''. Remarks ~~~~~~~ - Please note that the ordering of elements is defined by the ordering of the elements from fragment A'', followed by the ordering from the elements from fragment A', followed by the ordering from the elements from fragment A. - This implementation allows to spread the inheritance invocations across different files to maximize the reuse of XML fragments. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Please do not define a circular inheritance definition because this will result in an infinite recursive invocation during the generation process. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Implementation details ~~~~~~~~~~~~~~~~~~~~~~ There are four reserved attributes in a dedicated namespace (ooxml) that are used to implement the inheritance. These are: - ooxml:href is optional and refers to another file where the implementations to inherit from can be found. If not defined, the current file is assumed. - ooxml:extends is optional and refers to the implementation where is inherited from. This implementation can be found in the same file or in another file, depending on the optional ooxml:href attribute. - ooxml:implements is optional and defines the name of the implementation where other XML fragments can inherit from. - ooxml:instantiate is mandatory. The values of this attribute are 'yes' or 'no'. - 'yes' means that the inheritance instantiation is written to the output. - 'no' means that the inheritance instantiation is suppressed and not written to the output. Example: ~~~~~~~~ This is a small example about a fictious HRM department. The generation is invoked by the command line "msxsl in.xml transform.xsl -o out.xml" ******************************************************************************** ************* file in.xml *************** ******************************************************************************** <?xml version="1.0" encoding="utf-8"?>
<hrm xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance">
<person id="1"
ooxml:extends="defaultPerson"
ooxml:instantiate="yes">
<first-name>Mary</first-name>
<last-name>Jacobs</last-name>
<physical>
<hair>black</hair>
</physical>
<vehicle id="2"/>
</person> <person id="2"
manager="yes"
ooxml:extends="defaultPerson"
ooxml:instantiate="yes">
<first-name>Joe</first-name>
<physical>
<eyes>blue<comment>with some green spots</comment></eyes>
<weight unit="kg">82</weight>
</physical>
<title>Sales Manager</title>
<hrmRemark authorInitials="JC" date="20010929">CEO profile</hrmRemark>
</person> <person id="unknown"
manager="no"
ooxml:implements="defaultPerson"
ooxml:instantiate="no">
<first-name>Unknown</first-name>
<last-name>Unknown</last-name>
<physical>
<hair>Unknown</hair>
<eyes>Unknown</eyes>
<handicap>None</handicap>
</physical>
<title>Clerk</title>
<vehicle id="unknown"/>
</person> <vehicle id="1"
ooxml:href="in2.xml"
ooxml:extends="defaultTruck"
ooxml:instantiate="yes">
<licencePlate>XSL-402</licencePlate>
<make>Volvo</make>
<status>New</status>
<color>Green</color>
</vehicle> <vehicle ooxml:href="in2.xml"
ooxml:extends="defaultCar"
ooxml:instantiate="yes">
<licencePlate>XML-008</licencePlate>
<make>Ford</make>
<color>Red</color>
</vehicle><anyOtherNode>anyOtherNode</anyOtherNode> </hrm> ******************************************************************************** ************* file in2.xml *************** ******************************************************************************** <?xml version="1.0" encoding="utf-8"?>
<vehicleDefaults xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance">
<vehicle ooxml:implements="defaultTruck"
ooxml:extends="defaultVehicle"
ooxml:instantiate="yes">
<type>Truck</type>
<maxLoad>100</maxLoad>
</vehicle> <vehicle ooxml:implements="defaultCar"
ooxml:extends="defaultVehicle"
ooxml:instantiate="yes">
<type>Car</type>
<fuel ron="95">Petrol</fuel>
</vehicle> <vehicle ooxml:implements="defaultVehicle"
ooxml:extends="defaultFuel"
ooxml:instantiate="yes">
<licencePlate>Unknown</licencePlate>
<make>Unknown</make>
<year>Unknown</year>
<type>Unknown</type>
<status>Used</status>
</vehicle> <vehicle ooxml:implements="defaultFuel"
ooxml:instantiate="yes" id="undefined">
<fuel>Gasoline</fuel>
</vehicle><anyOtherNode>Any other node from in2.xml</anyOtherNode> </vehicleDefaults> ******************************************************************************** ************* file out.xml (after reformatting) *************** ******************************************************************************** <?xml version="1.0" encoding="utf-8"?>
<hrm xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance">
<person id="1" manager="no">
<first-name>Mary</first-name>
<last-name>Jacobs</last-name>
<physical>
<hair>black</hair>
<eyes>Unknown</eyes>
<handicap>None</handicap>
</physical>
<vehicle id="2" />
<title>Clerk</title>
</person> <person id="2" manager="yes">
<first-name>Joe</first-name>
<physical>
<eyes>blue
<comment>with some green spots</comment>
</eyes>
<weight unit="kg">82</weight>
<hair>Unknown</hair>
<handicap>None</handicap>
</physical>
<title>Sales Manager</title>
<hrmRemark authorInitials="JC" date="20010929">CEO profile</hrmRemark>
<last-name>Unknown</last-name>
<vehicle id="unknown" />
</person><vehicle id="1"> <licencePlate>XSL-402</licencePlate> <make>Volvo</make> <status>New</status> <color>Green</color> <type>Truck</type> <maxLoad>100</maxLoad> <year>Unknown</year> <fuel>Gasoline</fuel> </vehicle> <vehicle id="undefined"> <licencePlate>XML-008</licencePlate> <make>Ford</make> <color>Red</color> <type>Car</type> <fuel ron="95">Petrol</fuel> <year>Unknown</year> <status>Used</status> </vehicle> <anyOtherNode>anyOtherNode</anyOtherNode> </hrm> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="ooxml"> <xsl:output method="xml" indent="yes" encoding="utf-8"/> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!--
Only inherit and instantiate nodes from the the source file where the
ooxml:instantiate attribute has the value 'yes'.
-->
<xsl:template match="*[@ooxml:instantiate='yes']">
<xsl:variable name="result">
<xsl:call-template name="instantiate">
<xsl:with-param name="instance" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:copy-of select="$result"/>
</xsl:template><!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- Absorb all nodes from the source file where the attribute ooxml:instantiate has the value 'no'. --> <xsl:template match="*[@ooxml:instantiate='no']"/> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- Identity transformation for all the other nodes with their associated attributes. Also comments are copied to the output. All nodes from the source file without an attribute ooxml:instantiate or with an attribute ooxml:instantiate with values different from 'yes' or 'no' are copied to the output without any further processing. --> <xsl:template match="@*|*|comment()"> <xsl:copy> <xsl:apply-templates select="@*|node()|comment()"/> </xsl:copy> </xsl:template> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- Instantiate the selected node with single inheritance. The inheritance tree is traveled from an arbitrary child of the inheritance tree till the root. A leaf is represented by an XML element with the attribute ooxml:instantiate = 'yes' and possibly the ooxml:extends and ooxml:href attributes witch refers to an implementation to inherit from. --> <xsl:template name="instantiate"> <xsl:param name="instance"/> <xsl:variable name="temp">
<xsl:choose>
<!--
Is there an ooxml:href attribute associated with the ooxml:extends
attribute definition?
-->
<xsl:when test="string-length($instance/@ooxml:href) > 0">
<!--
Yes, there is an ooxml:href attribute. Retrieve the extending XML
fragment from this file.
-->
<xsl:call-template name="inherit">
<xsl:with-param name="extends" select=
"document($instance/@ooxml:href)//*[@ooxml:implements=$instance/@ooxml:extends]"/>
<xsl:with-param name="implements" select="$instance"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!--
No, there is no ooxml:href attribute. Retrieve the extending XML
fragment the current file.
-->
<xsl:call-template name="inherit">
<xsl:with-param name="extends"
select="//*[@ooxml:implements = $instance/@ooxml:extends]"/>
<xsl:with-param name="implements" select="$instance"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable><!-- Convert $temp to a node-set. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! This is the only MSXML3 dependency. This isn't too bad because an equivalent functionality is found in almost any 1.0 compliant XSLT processor. This is not needed for 1.1 and future higher XSLT processor implementations. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> <xsl:variable name="result" select="msxsl:node-set($temp)"/> <xsl:choose> <!-- Still another extension of the inheritance tree to process? --> <xsl:when test="string-length($result/*/@ooxml:extends) > 0"> <!-- Yes. Recurse until the inheritance root is reached. The root is an XML fragment without an ooxml:extends attribute or with an empty ooxml:extends attribute. --> <xsl:call-template name="instantiate"> <xsl:with-param name="instance" select="$result/*"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <!-- No. Stop recursion and copy the result from the inheritance instantiation to the output. --> <xsl:element name="{name()}"> <!-- Suppress all ooxml attributes to the output. --> <xsl:for-each select= "$result/*/@*[namespace-uri()!='http://www.barcoview.com/ooxml/inheritance']"> <xsl:attribute name="{name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:copy-of select="$result/*/*"/> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- The inherit template implements one inheritance resolution step of the complete inheritance chain resolution process. --> <xsl:template name="inherit"> <!-- The inherit function receives two parameters: - $extends holds the current node where is inherited from at this stage of the inheritance resolution process. - $implements holds the current instance of the inheritance as processed at this stage of the recursive invocation. --> <xsl:param name="extends"/> <xsl:param name="implements"/> <xsl:choose> <!-- Has $extends any children? --> <xsl:when test="$extends[*]"> <!-- Yes, $extends has one or more child elements. Only copy the root element from $extends with some selected attributes to keep the inheritance chain intact. --> <xsl:element name="{name()}"> <!-- If the current $extends has an ooxml:extends attribute, meaning that the inheritance chain continuous, get this ooxml:extends attribute together with the associated optional ooxml:href attribute. If not, the inheritance chain would be broken. The ooxml:implements and ooxml:instantiate attributes are explicit copied. All other attributes are copied. --> <xsl:for-each select= "$extends/@*[not(name()=ooxml:implements and name()=ooxml:instantiate)]"> <xsl:attribute name="{name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <!-- If there is none or an empty $extends/@ooxml:href attribute, use the $implements/@ooxml:href attribute, if available to keep track of the current referenced file. --> <xsl:if test= "string-length($extends/@ooxml:href)=0 and string-length($implements/@ooxml:href)!=0"> <xsl:attribute name="ooxml:href"> <xsl:value-of select="$implements/@ooxml:href"/> </xsl:attribute> </xsl:if> <!-- Copy all the none ooxml attributes from the root element from $implements. Please, keep in mind that earlier copied attributes from the root element of $extends with the same names as the $implements root element attributes are overwritten. This approach implements the inheritance resolution of the attributes. --> <xsl:for-each select= "$implements/@*[namespace-uri()!='http://www.barcoview.com/ooxml/inheritance']"> <xsl:attribute name="{name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <!--
Copy the text node from the current instantiation.
-->
<xsl:copy-of select="$implements/text()"/> <!--
Get all children elements with their descendants for the current
instantiation so far. This is implemented by invoking inherit
recursively until all descendants of a child are processed. This
process is repeated for each child.
-->
<xsl:for-each select="$implements/*">
<xsl:call-template name="inherit">
<xsl:with-param name="extends"
select="$extends/*[name()=name(current())]"/>
<xsl:with-param name="implements" select="."/>
</xsl:call-template>
</xsl:for-each><!-- Get all children elements with their descendants from the extending XML fragment, on the condition that these elements with the same name don't occur in the instantiation so far. This is implemented by copying all these children with their descendants to the instantiation. --> <xsl:for-each select="$extends/*"> <xsl:if test="not($implements/*[name()=name(current())])"> <xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </xsl:element> </xsl:when> <xsl:otherwise> <!-- No, $extends has no child elements any more. Stop the recursion and return the result of this stage of the inheritance resolution process. --> <xsl:element name="{name()}"> <xsl:for-each select="$extends/@*"> <xsl:attribute name="{name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:for-each select="$implements/@*"> <xsl:attribute name="{name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:copy-of select="$implements/* | $implements/text()"/> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> </xsl:stylesheet>
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
|

Cart



