Re: [xsl] filtering descendent text nodes

Subject: Re: [xsl] filtering descendent text nodes
From: Peter Davis <pdavis152@xxxxxxxxx>
Date: Mon, 18 Mar 2002 01:36:57 -0800
Child nodes are nodes that are contained within another element, and 
descendent nodes are children of children (of children... and so on).  Also, 
child of an element is also a descendent of that element, but not all 
descendents are children.

The problem here is that you have a template that matches "customer//node()", 
which is supposed to output the value of that node with xsl:value-of.  I see 
what you were trying to do, but it doesn't quite work like that.  It is doing 
exactly what you tell it to, but the node() match is not specific enough.

Here is what is happening when your stylesheet executes:

1) The root (in template match="/") is selected.  xsl:apply-templates is 
called on all the children of the root.

2) "custdet" is selected and matched by template match="*", and 
xsl:apply-templates is called on the children.

3) "customer" is selected by template match="*", xsl:apply-templates on the 
children.

4) "name" is selected by template match="customer//node()", because <name> is 
a descendent of <customer>.  It is also a child of <customer>, but all 
children are also descendents, and therefore the "//" applies to it as well.

So the HTML code and "First Name:" are output when the <name> element is 
selected.  Then, you call <xsl:value-of select="."/>, which outputs the text 
value of *all* of the descendent nodes of <name> put together.  This is why 
FIRST1, NAME1, and SECONDNAME1 are all output on the same line (well, not 
really the same line if you look at the HTML output's directly instead of in 
a browser, but I'll get to the whitespace problems in a minute).


The solution is to make your templates more specifically match what you want 
them to match.  If you want the result to be:

First Name: FIRST1
First Name: NAME1
First Name: SECONDNAME1

then instead of matching "customer//node()", you should match 
"customer//text()".  That way each text node within <customer> gets its own 
"First Name:" and associated HTML.  "customer//node()" is to general, since 
it matches the first node it finds, even though the node is a child as well 
as a descendent.

But since it does not appear that all of those names are actually first 
names, you will need to have different templates for each <firstname>, 
<secondname>, or <secondname1> element.  If you want them to still have the 
same HTML, but different text, then you can do it with a named template:

<!-- remove template match="customer//node()", since it is replaced by the 
following templates -->

<xsl:template match="firstname">
  <xsl:call-template name="output-name">
    <xsl:with-param name="description">First Name: </xsl:with-param>
  </xsl:call-template>
</xsl:template>

<xsl:template match="firstname">
  <xsl:call-template name="output-name">
    <xsl:with-param name="description">Second Name: </xsl:with-param>
  </xsl:call-template>
</xsl:template>

<xsl:template match="secondname1">
  <xsl:call-template name="output-name">
    <xsl:with-param name="description">Second Name 1: </xsl:with-param>
  </xsl:call-template>
</xsl:template>

<xsl:template name="output-name">
  <xsl:param name="description"/>
  <xsl:copy-of select="$description"/>
  <b><i>
    <font color="red">
      <xsl:value-of select="." />
      <br/>
    </font>
  </i></b>
</xsl:template>


The other problem you are seeing, and the reason why you are getting multiple 
"First Name:" outputs with empty content, is because the whitespace in the 
source document is being matched by "customer//node()" just as the <name> is. 
 I'm talking about the whitespace between <customer> and <name> and between 
</name> and </customer>.

If you use something like the above example templates, the problem will go 
away because those templates are more specific about what they match.  But if 
you still use your current templates, you will have to add <xsl:strip-space 
elements="*"> to the top of your stylesheet to remove text nodes that contain 
only whitespace.

Hope this helps!

On Sunday 17 March 2002 19:09, Aseef Jamaluddin wrote:
> hi,
> i am new to xslt. Given below is the files i am using.
>
> <?xml version="1.0" ?>
> <custdet>
> <customer>
> 	<name>
> 	<firstname>FIRST1</firstname>
> 	<secondname>NAME1
> 		<secondname1>SECONDNAME1</secondname1>
> 	</secondname>
> 	</name>
> </customer>
> </custdet>
>
> -------------------
> I AM TRYING TO FILTER OUT ALL DESCENDENT TEXT NODES
> VALUES (I.E. FIRST1,NAME1,SECONDNAME1) AND AM USING
> THE FOLLOWING XSL FOR THAT.
> -------------------
>
> <?xml version="1.0"?>
> <xsl:stylesheet
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> version="1.0">
> 	<xsl:template match="/">
> 		<xsl:apply-templates  />
> 	</xsl:template>
> 	<xsl:template match="*">
> 		<xsl:apply-templates/>
> 	</xsl:template>
> 	<xsl:template match="text()">
> 			<xsl:apply-templates/>
> 	</xsl:template>
> <!-- -->
> <xsl:template match="customer//node()">
> First Name :
> <b><i>
> <font color="red">
> <xsl:value-of select="." />
> <br/>
> </font>
> </i></b>
>
> </xsl:template>
> <!---->
> </xsl:stylesheet>
>
>
> THE expected result is three lines with "FIRST NAME:"
> prefix and the corresponding text value attached. The
> output i am getting is as follows
>
>
> First Name :
> First Name : FIRST1 NAME1 SECONDNAME1
> First Name :
> First Name : 20one1
> First Name
>
> Could somebody explain what actually is happening and
> also some light on the difference between child nodes
> and descendent nodes.
>
> Thanks
> Aseef.J
>
> __________________________________________________
> Do You Yahoo!?
> Yahoo! Sports - live college hoops coverage
> http://sports.yahoo.com/
>
>  XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list

-- 
Peter Davis
If at first you don't succeed, quit; don't be a nut about success.

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


Current Thread