Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
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 software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old December 3rd, 2009, 06:29 AM
Friend of Wrox
 
Join Date: Oct 2003
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>
 
Old December 3rd, 2009, 08:24 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 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
The Following User Says Thank You to Martin Honnen For This Useful Post:
pallone (December 3rd, 2009)
 
Old December 3rd, 2009, 10:32 AM
Friend of Wrox
 
Join Date: Oct 2003
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
 
Old December 3rd, 2009, 01:08 PM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 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
 
Old December 4th, 2009, 06:46 AM
Friend of Wrox
 
Join Date: Oct 2003
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
 
Old December 4th, 2009, 07:33 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 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
The Following User Says Thank You to mhkay For This Useful Post:
pallone (December 4th, 2009)
 
Old December 4th, 2009, 07:37 AM
samjudson's Avatar
Friend of Wrox
 
Join Date: Aug 2007
Posts: 2,128
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?
The Following User Says Thank You to samjudson For This Useful Post:
pallone (December 4th, 2009)
 
Old December 4th, 2009, 11:25 AM
Friend of Wrox
 
Join Date: Oct 2003
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





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 08:07 AM
Recursion lincsimp XSLT 1 August 16th, 2005 04:26 PM
recursion yui0329 C# 6 April 28th, 2005 09:36 AM
MDI Sibling calling Sibling... Nick.Net VB.NET 2002/2003 Basics 1 December 8th, 2003 09:23 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.