Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > XML > XSLT
Password Reminder
Register
Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developersí questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Display Modes
  #1 (permalink)  
Old December 3rd, 2009, 06:29 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default SIBLING RECURSION - REVISITED

Hi,

Last week Michael and Martin kindly helped me and showed me how to use sibling recursion to display report headings in a web page.

I am now trying to use the same technique to add the report headers to an excel report but the output is not quite right.

I need the Frequency BW: BW Selected and Color: Color Selected to be displayed in the same CELL tag with a comma separating their values instead of using two CELL tags.

The output I am getting adds to Cell tags to the same Row like this:

<Row>
<Cell>
<Data ss:Type="String">Frequency BW: BW Selected</Data>
</Cell>, <Cell>
<Data ss:Type="String">Color: Color Selected</Data>
</Cell>
</Row>

The required output is this:

<Row>
<Cell>
<Data ss:Type="String">Frequency BW: BW Selected</Data>,
<Data ss:Type="String">Color: Color Selected</Data>
</Cell>
</Row>

I tried to pass a parameter to the template so that I could detect when to include a new Cell but it did not work.

<xsl:template match="Parameter[@col = 2 and @row = preceding-sibling::Parameter[1]/@row]">
<xsl:text>, </xsl:text>
<xsl:apply-templates select=". | following-sibling::Parameter[1][@col = 2 and @row = current()/@row]" mode="m1">
<xsl:with-param name ="noCell" select ="1"></xsl:with-param>
</xsl:apply-templates>
</xsl:template>


I would appreciate very much if you could shed some light on how to achieve the desired output.

This is the XLST
--------------------------
<xsl:template match="ReportParams">
<xsl:apply-templates select="Parameter[1]"/>
</xsl:template>

<xsl:template match="Parameter[@col = 2 and @row = preceding-sibling::Parameter[1]/@row]">
<xsl:text>, </xsl:text>
<xsl:apply-templates select=". | following-sibling::Parameter[1][@col = 2 and @row = current()/@row]" mode="m1"/>
</xsl:template>

<xsl:template match="Parameter[not(@col = 2 and @row = preceding-sibling::Parameter[1]/@row)]">
<Row>
<xsl:apply-templates select="." mode="m1"/>
<xsl:apply-templates select="following-sibling::Parameter[1][@col = 2 and @row = current()/@row]"/>
</Row>
<xsl:apply-templates select="following-sibling::Parameter[not(@col = 2 and @row = current()/@row)][1]"/>
</xsl:template>

<xsl:template match="Parameter" mode="m1">
<xsl:choose>
<xsl:when test ="@title = 'true'">
<Cell ss:StyleID="bd">
<Data ss:Type="String">
<xsl:value-of select="@name"/>
<xsl:if test="@name != '' and @value != ''">
<xsl:text>: </xsl:text>
</xsl:if>
<xsl:value-of select="@value"/>
</Data>
</Cell>
</xsl:when>
<xsl:otherwise>
<Cell>
<Data ss:Type="String">
<xsl:value-of select="@name"/>
<xsl:if test="@name != '' and @value != ''">
<xsl:text>: </xsl:text>
</xsl:if>
<xsl:value-of select="@value"/>
</Data>
</Cell>
</xsl:otherwise>
</xsl:choose>


XML
------

<ReportParams>
<Parameter name="Study Title" value="" title="true" col="1" row="1" />
<Parameter name="" value="reader type selected" col="1" row="2"/>
<Parameter name="" value="exposure type selected" col="1" row="3"/>
<Parameter name="Demo" value="demo selected" col="1" row="4"/>
<Parameter name="Rx Class" value="Rx Class Selected" col="1" row="5"/>
<!-- for efficiency report -->
<Parameter name="Cost" value="" col="2" title="true" row="1" />
<Parameter name="A-Size" value="ad page selected" col="2" row="2"/>
<Parameter name="Tabloid" value="ad page selected" col="2" row="3" />
<Parameter name="Frequency BW" value="BW Selected" col="2" row="4" />
<Parameter name="Color" value="Color Selected" col="2" row="4" />
<Parameter name="CPM Emphasis" value="50" col="2" row="5" />
</ReportParams>


This is the full wrong output I current get
---------------------------------------------------------

<Table x:FullColumns="1" x:FullRows="1">
<Row>
<Cell ss:StyleID="bd">
<Data ss:Type="String">Study Title</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">reader type selected</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">exposure type selected</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">Demo: demo selected</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">Rx Class: Rx Class Selected</Data>
</Cell>
</Row>
<Row>
<Cell ss:StyleID="bd">
<Data ss:Type="String">Cost</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">A-Size: ad page selected</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">Tabloid: ad page selected</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">Frequency BW: BW Selected</Data>
</Cell>, <Cell>
<Data ss:Type="String">Color: Color Selected</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">CPM Emphasis: 50</Data>
</Cell>
</Row>
<Row />
</Table>
Reply With Quote
  #2 (permalink)  
Old December 3rd, 2009, 08:24 AM
Friend of Wrox
Points: 6,525, Level: 34
Points: 6,525, Level: 34 Points: 6,525, Level: 34 Points: 6,525, Level: 34
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Nov 2007
Location: Germany
Posts: 1,221
Thanks: 0
Thanked 238 Times in 237 Posts
Default

I took the stylesheet I posted in the previous thread and slightly adapted it:
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
  
  <xsl:output method="xml" indent="yes"/>
  
  <xsl:template match="ReportParams">
    <Table>
      <xsl:apply-templates select="Parameter[1]"/>
    </Table>
  </xsl:template>
  
  <xsl:template match="Parameter[@col = 2 and @row = preceding-sibling::Parameter[1]/@row]">
    <xsl:text>, </xsl:text>
    <xsl:apply-templates select=". | following-sibling::Parameter[1][@col = 2 and @row = current()/@row]" mode="m1"/>
  </xsl:template>
  
  <xsl:template match="Parameter[not(@col = 2 and @row = preceding-sibling::Parameter[1]/@row)]">
    <Row>
      <Cell>
        <xsl:apply-templates select="." mode="m1"/>
        <xsl:apply-templates select="following-sibling::Parameter[1][@col = 2 and @row = current()/@row]"/>
      </Cell>
    </Row>
    <xsl:apply-templates select="following-sibling::Parameter[not(@col = 2 and @row = current()/@row)][1]"/>
  </xsl:template>
  
  <xsl:template match="Parameter" mode="m1">
    <Data>
      <xsl:choose>
        <xsl:when test ="@title = 'true'">
          <b><xsl:value-of select="@name"/></b>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="@name"/>
        </xsl:otherwise>
      </xsl:choose>
    
      <xsl:if test="@name != '' and @value != ''">
        <xsl:text>: </xsl:text>
      </xsl:if>
      
      <xsl:value-of select="@value"/>
    </Data>
  </xsl:template>

</xsl:stylesheet>
The result, when run against the input data
Code:
<ReportParams>
<Parameter name="Study Title" value="" title="true" col="1" row="1" />
<Parameter name="" value="reader type selected" col="1" row="2"/>
<Parameter name="" value="exposure type selected" col="1" row="3"/>
<Parameter name="Demo" value="demo selected" col="1" row="4"/>
<Parameter name="Rx Class" value="Rx Class Selected" col="1" row="5"/>
<!-- for efficiency report -->
<Parameter name="Cost" value="" col="2" title="true" row="1" />
<Parameter name="A-Size" value="ad page selected" col="2" row="2"/>
<Parameter name="Tabloid" value="ad page selected" col="2" row="3" />
<Parameter name="Frequency BW" value="BW Selected" col="2" row="4" />
<Parameter name="Color" value="Color Selected" col="2" row="4" />
<Parameter name="CPM Emphasis" value="50" col="2" row="5" />
</ReportParams>
is as follows:
Code:
<Table>
   <Row>
      <Cell>
         <Data>
            <b>Study Title</b>
         </Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>reader type selected</Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>exposure type selected</Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>Demo: demo selected</Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>Rx Class: Rx Class Selected</Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>
            <b>Cost</b>
         </Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>A-Size: ad page selected</Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>Tabloid: ad page selected</Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>Frequency BW: BW Selected</Data>, <Data>Color: Color Selected</Data>
      </Cell>
   </Row>
   <Row>
      <Cell>
         <Data>CPM Emphasis: 50</Data>
      </Cell>
   </Row>
</Table>
That seems to be what you want, as far as the general structure of Row/Cell/Data is concerned. You will need to add the ss:Type attributes for instance but I hope you can solve that yourself. If not then you need to tell which namespace you want the bind the prefix 'ss' to.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
Reply With Quote
The Following User Says Thank You to Martin Honnen For This Useful Post:
pallone (December 3rd, 2009)
  #3 (permalink)  
Old December 3rd, 2009, 10:32 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Hi Martin,

Many thanks for your help.

I tried your code but I got an parser error:

XML ERROR in Table
REASON: Too many tags
FILE: C:\Documents and Settings\pallonec\Local Settings\Temporary Internet Files\Content.IE5\4YBMJN9K\report[2].xls
GROUP: Cell
TAG: Data
VALUE: Color: Color Selected

I then realised that I cannot have two <Data> tags inside the same <Cell> tag so I removed the Data tag from the last template and placed it inside the previous one. I also added a check to print the title in bold and it is all working now.

<xsl:template match="Parameter[not(@col = 2 and @row = preceding-sibling::Parameter[1]/@row)]">
<Row>
<xsl:choose>
<xsl:when test ="@title = 'true'">
<Cell ss:StyleID="bd">
<Data ss:Type="String">
<xsl:apply-templates select="." mode="m1"/>
<xsl:apply-templates select="following-sibling::Parameter[1][@col = 2 and @row = current()/@row]"/>
</Data>
</Cell>
</xsl:when>
<xsl:otherwise>
<Cell>
<Data ss:Type="String">
<xsl:apply-templates select="." mode="m1"/>
<xsl:apply-templates select="following-sibling::Parameter[1][@col = 2 and @row = current()/@row]"/>
</Data>
</Cell>
</xsl:otherwise>
</xsl:choose>
</Row>
<xsl:apply-templates select="following-sibling::Parameter[not(@col = 2 and @row = current()/@row)][1]"/>
</xsl:template>

I have to confess that I am still finding difficult to get my head around the sibling recursion.

Could you please clarify the following points?

1 - If inside this template, I call a named template:

<xsl:template match="Parameter" mode="m1">

<xsl:choose>
<xsl:when test ="self::node()[@col = 2 and @row = following-sibling::Parameter[1]/@row]">

<xsl:call-template name ="noCellAdded">
<xsl:with-param name ="followingSib" select ="following-sibling::Parameter[1][@col = 2 and @row = current()/@row]"/>
</xsl:call-template>

</xsl:when>
</xsl:choose>

</xsl:template>

How do I get the values of of the $noCellAdded node set? Also how do I call templates from there. I have tried the code below but I cannot get the values from the node set.

<xsl:template name ="noCellAdded">
<xsl:param name ="followingSib"></xsl:param>
<xsl:choose>
<xsl:when test ="followingSib/@title = 'true'">
<Data ss:Type="String">
<xsl:value-of select="followingSib/@value"/>
</Data>
</xsl:when>
<xsl:otherwise>
<Data ss:Type="String">
<xsl:value-of select="followingSib/@value"/>
</Data>
</xsl:otherwise>

</xsl:choose>
<!--<xsl:apply-templates select=". | following-sibling::Parameter[1][@col = 2 and @row = current()/@row]" mode="m1"/>-->

<xsl:apply-templates select="following-sibling::followingSib[@col = 2 and @row = current()/@row]" mode="m1"/>
</xsl:template>

2 - I still cannot understand why you put the index [1] at the end of your expression!!!

<xsl:apply-templates select="following-sibling::Parameter[not(@col = 2 and @row = current()/@row)][1]"/>

Could the [1] be placed after the Parameter tag like this:

<xsl:apply-templates select="following-sibling::Parameter[1][not(@col = 2 and @row = current()/@row)]"/>

3 - why do you apply template with a unions? We are only interested in the following subling, arent we?

<xsl:apply-templates select=". | following-sibling::Parameter[1][@col = 2 and @row = current()/@row]" mode="m1"/>

Cheers

C
Reply With Quote
  #4 (permalink)  
Old December 3rd, 2009, 01:08 PM
Friend of Wrox
Points: 6,525, Level: 34
Points: 6,525, Level: 34 Points: 6,525, Level: 34 Points: 6,525, Level: 34
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Nov 2007
Location: Germany
Posts: 1,221
Thanks: 0
Thanked 238 Times in 237 Posts
Default

As for your first question, I am not sure what you are asking: the template you can is named 'noCellAdded', it takes a parameter named 'followingSib' so inside the template you can used that parameter with '$followingSib'. Why you think there should be a 'noCellAdded' node set I don't understand, as that is simply the name of the template.

As for your second question, where you put the index [1], I did already explain that. If you have following-sibling::Parameter[1][someCondition] then you select the first following sibling 'Parameter' element if it fullfills the condition. If you have it the other way round e.g. following-sibling:Parameter[someCondition][1] then you select the first following sibling 'Parameter' element for which the condition holds.

As for the third question and the apply-templates on the union, that is necessary as there are different modes used and the template you are talking about only outputs the comma and then needs to ensure the data of the current 'Parameter' element is output as well so it needs to be ensured that the context node and the following sibling is selected for processing in the other mode.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
Reply With Quote
  #5 (permalink)  
Old December 4th, 2009, 06:46 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Hi Martin,

2 - As for question 2 ([1]), I am sorry to have asked the same question again. I might have a mental block because I cannot get it.

I cannot distinguish the diffence between these two statements:

following-sibling::Parameter[1][someCondition]
"then you select the first following sibling 'Parameter' element if it fullfills the condition."

following-sibling:Parameter[someCondition][1]
"select the first following sibling 'Parameter' element for which the condition holds."

In the second statement, are you saying that it will look for all the following siblings that mathc the condition i.e not only the very next one in document order?

Please could you provide a simple example so that I can understand it?


1 - As for my first question I am sorry if it was confusing. I will try to make it clearer.

Inside this template we have a collection of Parameter elements

<xsl:template match="ReportParams">
<xsl:apply-templates select="Parameter[1]"/>
</xsl:template>

When we apply templates we are passing only the first element. So, I assume that in the matching template we will no longer have a collection but only one Parameter element.

However, how is it possible that I can use following-sibling::Parameter [1] inside this template if I only have one Parameter element and no longer a collection?

<xsl:template match="Parameter" mode="m1">
<xsl:choose>
<xsl:when test ="self::node()[@col = 2 and @row = following-sibling::Parameter [1]/@row]">

<xsl:call-template name ="noCellAdded">
<xsl:with-param name ="followingSib" select ="following-sibling::Parameter[1][@col = 2 and @row = current()/@row]"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>

</xsl:template>

Also, after I call the named template passing the the following sibling (which l find it strange since I no longer have a collection), how can I get the value of the attributes and apply templates to the following::siblings? - if it is possible?

I have tried this - followingSib/@title and this - followingSib[1]/@title - but to no avail.

I have also tried to apply templates to the following sibling inside the named template but it does not work

<xsl:apply-templates select="following-sibling::followingSib[@col = 2 and @row = current()/@row]" mode="m1"/>

<xsl:template name ="noCellAdded">
<xsl:param name ="followingSib"></xsl:param>
<xsl:choose>
<xsl:when test ="followingSib/@title = 'true'">
<Data ss:Type="String">
<xsl:value-of select="followingSib/@value"/>
</Data>
</xsl:when>
<xsl:otherwise>
<Data ss:Type="String">
<xsl:value-of select="followingSib/@value"/>
</Data>
</xsl:otherwise>


Does it make sense?

Thanks for your help.

Cheers

C
Reply With Quote
  #6 (permalink)  
Old December 4th, 2009, 07:33 AM
mhkay's Avatar
Wrox Author
Points: 18,280, Level: 58
Points: 18,280, Level: 58 Points: 18,280, Level: 58 Points: 18,280, Level: 58
Activity: 33%
Activity: 33% Activity: 33% Activity: 33%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,934
Thanks: 0
Thanked 282 Times in 277 Posts
Default

>I cannot *distinguish the diffence between these two statements*:

following-sibling::Parameter[1][someCondition]
"then you select the first following sibling 'Parameter' element* if it fullfills the condition*."

following-sibling:Parameter[someCondition][1]
"select the first following sibling 'Parameter' element *for which the condition holds*."

This is pretty fundamental. Consider the following sequence $seq

A B C D E

(1) $seq[.='C'][1]

looks for all the items equal to C and returns the first of these, that is the third item in the original sequence

(2) $seq[1][.='C']

selects the first item in the original sequence, and then tests whether it is equal to "C". It isn't, so it returns nothing.

It's a little unfortunate that XPath overloads the [] notation to mean two different things. If instead it used # for subscripting and [] for filtering, then it might be clearer that ($seq#1)[condition] and ($seq[condition])#1 were very different expressions.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
Reply With Quote
The Following User Says Thank You to mhkay For This Useful Post:
pallone (December 4th, 2009)
  #7 (permalink)  
Old December 4th, 2009, 07:37 AM
samjudson's Avatar
Friend of Wrox
Points: 8,687, Level: 40
Points: 8,687, Level: 40 Points: 8,687, Level: 40 Points: 8,687, Level: 40
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Aug 2007
Location: Newcastle, , United Kingdom.
Posts: 2,133
Thanks: 1
Thanked 189 Times in 188 Posts
Default

1)

Take the following example XML:

<Root>
<Y z="1">
<Y z="2">
<Y z="3">
<Y z="4">
<Y z="3">
</Root>

If you select /Root/Y[1] this will return the first Y element, where z=1.
If you select /Root/Y[z=3] this will return the 2 elements where z=3, the third and fifth Y elements.
If you select /Root/Y[1][z=3] this will return nothing, because it selects all elements where z=3 within the 'set' of the first element of Y (where z=1).
It you select /Root/Y[z=3][1] this will return the first element where z=3, i.e. the third Y element.

2)

When you call apply-template you are never passing a 'collection' to a template, you are telling the XSLT processor to process each element that matches the select clause with the correct template. This all has to do with the current 'context'. Imagine you are simply pointing at the relevant node within the entire document and saying "process this".

3)

When you want to use the parameter followingSib you need to refer to it with as $followingSib.
__________________
/- Sam Judson : Wrox Technical Editor -/

Think before you post: What have you tried?
Reply With Quote
The Following User Says Thank You to samjudson For This Useful Post:
pallone (December 4th, 2009)
  #8 (permalink)  
Old December 4th, 2009, 11:25 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Hi Michael and samjudson

Many thanks for your explanation. I think I am starting to get it around my head now.

I really appreciate your help

Cheers

Claudio
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off

Similar Threads
Thread Thread Starter Forum Replies Last Post
Want to Pull multiple following-sibling data to preceding-sibling element sameer_kadam XSLT 4 May 9th, 2009 09:07 AM
Recursion lincsimp XSLT 1 August 16th, 2005 05:26 PM
recursion yui0329 C# 6 April 28th, 2005 10:36 AM
MDI Sibling calling Sibling... Nick.Net VB.NET 2002/2003 Basics 1 December 8th, 2003 09:23 PM



All times are GMT -4. The time now is 11:48 AM.


Powered by vBulletin®
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.