Re: [xsl] XSL resources - Flat to hierarchy - Common ancestors

Subject: Re: [xsl] XSL resources - Flat to hierarchy - Common ancestors
From: "M. David Peterson" <m.david@xxxxxxxxxx>
Date: Tue, 27 Jul 2004 03:57:18 -0600
Hey Ben,

Brutal honesty... gotta love it :)

The simplest way to both explain and to understand what apply-templates does is as such. The following XML is a representation of circles, squares, and triangles. Each shape has the ability to be one of three colors: red, yellow, and blue. As such there is a possibility of 9 different combinations of of colors and shapes. Youve been given the task to find all shapes that are yellow and put them in the basket labeled yellow. One other thing: Im also going to create a "Path" reference that represents the structure of the XML if which we will first make a copy of its current state and then simply subtract from that copy either a "/" or an element name and its following "/" seperator each time we use the match attribute to match an element or attribute. This way we can keep track of where we are in context to the overall XML structure.

The XML:

  <shape type="triangle" color="red"/>
  <shape type="triangle" color="yellow"/>
  <shape type="triangle" color="blue"/>
  <shape type="square" color="red"/>
  <shape type="square" color="yellow"/>
  <shape type="square" color="blue"/>
  <shape type="circle" color="red"/>
  <shape type="circle" color="yellow"/>
  <shape type="circle" color="blue"/>

The XPath:


Keep in mind that unless we specify otherwise the last element specified in an XPath statement will grab its attributes and children and pack them up for the ride ahead of them.

Now we'll use XSLT to represent two things:

You (your job being to go through each shape, find those that are yellow, and put them in the basket labeled yellow).


The basket labeled yellow

<xsl:template match="/">
<!-- this template represents you -->

<!-- because we used the match element to select the root of the tree structure we make a copy of the above XPath -(minus) the first "/". So now we have "shapes/shape" of which we make a mental copy of and take with us to look at later -->

<!-- the following "apply-templates" element represents the mechanisms of deciding which shapes match your job criteris and any other specific information necessary to determine what to do with the shapes that have met your criteria. The "select" attribute does basically what it sounds like... it selects XML elements and/or attributes that match the instructions contained inside of it... The following apply-templates element will pick out ALL shape elements who's color is equal to yellow -->

<xsl:apply-templates select="shapes/shape[@color = 'yellow']" />

<!-- what we have just done is told the processor to take all elements (keep the copy of the XPath in your head while writing the above statement to ensure you use the right syntax to select the right elements and/or attributes) that have a color attribute that is equal to yellow and then go through each template in the XSLT tree structure and use the match attribute to find the closest match to each selected element and stick that element (remember, its still packing the attributes and its children right along with it) into the template whos match attribute is the closest in describing that element, its attributes, and/or its children. Our Result Tree Fragment of elements that have been selected by the processor looks like this:

<shape type="triangle" color="yellow"/>
<shape type="square" color="yellow"/>
<shape type="circle" color="yellow"/>

Keep in mind that apply-templates is'nt going anywhere. It takes each element and sends it out into the processors ether to be matched to its closest matching template. When apply-templates has finished its list of elements the processor then looks to the next element after apply-templates which in our case is nothing, so the process as far as this template is concerned is done... well, unless the closest match to an element is this template... then this template has to decide what to do with the matching element in the same way it had to decide what to do with all the elements that matched it the first time round when "/" matched the root element of our XML tree.

So, lets go see what happened to all of the elements we just sent out of here...



<xsl:template match="shape[@color = 'red']">
  I am a red <xsl:value-of select="@shape"/>. <br/>

<xsl:template match="shape[@color = 'yellow']">
  I am a yellow <xsl:value-of select="@shape"/>. <br/>

<xsl:template match="shape[@color = 'blue']">
  I am a blue <xsl:value-of select="@shape"/>. <br/>

As should be fairly obvious each of the elements selected by the XPath contained in the "select" attribute of the apply-templates element was passed into the processors ether and matched against each templates match attribute. The match attribute that best described the element at hand was then given the honor of handling this element as it so pleased... in this case its pleasure what to write to the output stream "I am a yellow " and then name of the shape contained in the "shape" attribute. And if you remembered to make a copy of the XPath and then subtracted the element (keep in memory a copy of its name, value, any attributes, and its children but erase from our "frame of reference" the XPath contained in the match attribute so that we can remember how to properly reference any child elements and attributes as we continue through the process. In this case the only thing we have left is the attributes so our frame of reference is now empty... but if there were any children... say a child element called "size"... then after subtracting "shape" and the following "/" seperator from our frame of reference we have "size" as the remaining element(s) structure referenced in your new copy of the XPath frame of reference... this is important to remember if you decide that you want to use another apply-templates inside of the "yellow" template and match all "size" elements to another template somewhere in our tree structure... but for now we'll just leave it as is and call the job complete.

So, after all is said and done by the matching "yellow" template our output looks like:

I am a yellow triangle. <br/>
I am a yellow square. <br/>
I am a yellow circle. <br/>

Ok, thats the basics of apply-templates... I hope this helps in your understanding!!!! I realize this was a REALLY basic way to describe things and as such I should emphasize that I am in no way trying to make light of anything or anybody... This is just the simplest way to describe how apply-temnplates works and I have found that by using this or something similar to it people have a tendency to immediatelly say "ah! got it! :)" and then are able to take it from there...

Best of luck to you!



Ben Simkins wrote:

2. Transforming 'flat' structures to hierarchies:


Wrap the first apply-templates into a variable, use your processors


function (see your processors documentation on how to implment this)


the result into a node-set and then apply-templates or for-each


resulting node-set using <xsl:number/> to accomplish the task

I'm supposed to be doing something else, but I just had to try this out :) Having 'learnt' XSL by cut and paste, I think I'm still at the stage where I need subtitles. I added xmlns:msxsl="urn:schemas-microsoft-com:xslt" to my xsl:stylesheet declaration, and my first template now looks like this:

<xsl:template match="/">
<xsl:variable name="menus">
<xsl:apply-templates select="Menus"/>
<xsl:for-each select="msxsl:node-set($menus)">
<xsl:copy-of select="@*"/>
<xsl:attribute name="TOC">
<xsl:number level="multiple" format="1.1"/>
</xsl:attribute> </xsl:copy>
</xsl:for-each> </xsl:template>

Obviously, it doesn't work, otherwise I wouldn't be posting. I guess
what I don't understand is whether my for-each is for each node in the
nodeset.  You suggested mayb using an apply-templates, but I don't
really understand how to do an apply templates which won't eat my other
menus upstream (ok, I'll be honest, I just don't understand
apply-templates ;)


Current Thread