This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: filtering descendent text nodes
- From: Peter Davis <pdavis152 at attbi dot com>
- To: xsl-list at lists dot mulberrytech dot com
- Date: Mon, 18 Mar 2002 01:36:57 -0800
- Subject: Re: [xsl] filtering descendent text nodes
- References: <20020318030918.8392.qmail@web14604.mail.yahoo.com>
- Reply-to: xsl-list at lists dot mulberrytech dot com
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