[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] different first element in a list


Subject: Re: [xsl] different first element in a list
From: Mike Brown <mike@xxxxxxxx>
Date: Mon, 24 Feb 2003 10:54:51 -0700 (MST)

Lorenzo De Tomasi wrote:
> on 24-02-2003 10:07, Michael Kay at michael.h.kay@xxxxxxxxxxxx wrote:
> 
> > In the inner for-each, instead of
> > 
> > xsl:for-each select="key('k', @type)"
> > 
> > try
> > 
> > xsl:for-each select="key('k', @type)[position()!=1]"
> 
> Now it works :) Thank you very much...
> but I still have a lot of problems in understanding how this works
> Can somebody help me to understand commenting the code?
> The Xslt I is at
> <http://biografica.tzone.it/cv/eucv_eng_prova.xsl>)

The XSLT processing model is what you should know first:

  - there's an input node tree representing the meat of an XML document,

  - instructions in the best matching XSLT template for that tree's root node 
    are instantiated,

  - those instructions, depending on what they are, result in the selection 
    and processing of more nodes, or the observation of different parts of the 
    input tree, or the creation of nodes in an output node tree (result tree),

  - the result tree may be serialized in XML or HTML or plain text syntax
    according to another fixed algorithm, guided by hints one provides
    in the attributes of xsl:output and xsl:stylesheet elements.

There are some default templates that match each type of node, so that if you
don't put any templates in your stylesheet, some processing will still take
place.

The built-in templates for the root and element nodes contain just one
instruction: <xsl:apply-templates/>, which causes all child nodes of the
current node to be selected and processed. The built-in template for a text
node copies the text node to the result tree. So by default, a stylesheet with
no explicit templates will recursively descend through the source tree, 
copying only the text nodes to the output.

It may also help you to visualize the node trees. Try any of these 
stylesheets, or perhaps an XML editor that offers a tree view:

  http://skew.org/xml/stylesheets/treeview/ascii/
  http://skew.org/xml/stylesheets/treeview/html/
  http://www.cranesoftwrights.com/resources/showtree/

> Let's start from the basic concepts:
> If I have an xml list like this
> 
> __________________________________________
> Xml
> 
> <xml>
>     <element>1</element>
>     <element>3</element>
>     <element>2</element>
>     <element>4</element>
>     <element>5</element>
> </xml>

Well, you wouldn't have an element named 'xml' because 'xml' is not supposed
to be used at the beginning of an element or attribute name. Let's say 'data'
instead.

> __________________________________________
> 
> how can I obtain an ordered list like this
> 
> __________________________________________
> Xhtml
> 
> <table>
>     <tr>
>         <td>list:</td>
>         <td>1</td>
>     </tr>
>     <tr>
>         <td/>
>         <td>2</td>
>     </tr>
>     <tr>
>         <td/>
>         <td>3</td>
>     </tr>
>     <tr>
>         <td/>
>         <td>4</td>
>     </tr>
>     <tr>
>         <td/>
>         <td>5</td>
>     </tr>
> </table>

Look at the relationship between the output and the input. There is a table
row, with some content, for each 'element' element. So you know that when
processing an 'element' element, you can generate a tr element:

<xsl:template match="element">
  <tr>
    ...
  </tr>
</xsl:template>

The match attribute indicates that the template can be used for processing an
element node named 'element'. OK now when you're processing one of these
nodes, you can use it as a starting point for finding the rest of the data you 
need to fill up the table row. In this case, you can do it simply with

<xsl:template match="element">
  <tr>
    <td>
      <xsl:value-of select="."/>
    </td>
  </tr>
</xsl:template>

Selecting "." selects a node-set consisting of just the context node (the
'element' element being processed), and xsl:value-of creates a text node from
the string-value of the first node (in document order) of the selected set.
The string-value of an element is the concatenation of its descendant
text nodes. e.g., if you had <elem>hello <foo>bar</foo> world</elem>, then the
string-value of the 'elem' element would be "hello bar world". The XPath spec 
explains each type of node and how its string-value is determined.

OK well we didn't put the first column in the table. Again, look at the 
relationship between output and input. There is always a first table cell, but 
it only has the content 'list:' when it is in the first row. So you want to 
change the template so that when it is generating the first table row, it will 
output that text in the first td. How do you know when the template is 
generating the first row?

Again, look to the context node (the 'element' element currently being
processed). One of the things you know about the context node is its relative
position in the "current node list", which is a sequence of nodes that have
been selected for processing by xsl:apply-templates or xsl:for-each. If its
position in that list is 1, that's when you want to generate the 'list:' text. 
So here is the final version of your template:

<xsl:template match="element">
  <tr>
    <td>
      <xsl:if test="position()=1">
        <xsl:text>list:</list>
      </xsl:if>
    </td>
    <td>
      <xsl:value-of select="."/>
    </td>
  </tr>
</xsl:template>

Alright now you can generate a complete table row each time you process an 
'element' element. Now you have to consider that processing always starts at 
the root node (one level above the 'data' document element in our revised 
input). When processing the root node, you want to generate a table:

<xsl:template match="/">
  <table>
    ...
  </table>
</xsl:template>

The content of the table will be a set of rows: the result of processing all
those 'element' elements! If the context node is the root node, then to get
from there to the set of all 'element' elements, the XPath expression is 
(minus the quotes) "child::data/child::element" which can be written simply
as "data/element". To select them for processing and use the best-matching 
template for each one, with the entire set being the current node list each 
time, use xsl:apply-templates:

<xsl:template match="/">
  <table>
    <xsl:apply-templates select="data/element"/>
  </table>
</xsl:template>

And there you go. 2 simple templates that produce the output you want.

Mike

-- 
  Mike J. Brown   |  http://skew.org/~mike/resume/
  Denver, CO, USA |  http://skew.org/xml/

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



Current Thread