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

Re: [xsl] super basic xsl question


Subject: Re: [xsl] super basic xsl question
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 13 Jan 2005 13:45:54 -0500

Hi Jeb,

At 01:10 PM 1/13/2005, you wrote:
In this particular case, though, I want the template that matches the value of <child> to be agnostic to the markup that is in there, with no more template processing, I just want whatever was already done (by this point, that chunk of xml has been processed by other sheets, and will be processed by later sheets) so I didn't want to do an explicit match for the link tag.

That's the particular case that JBryant inferred, and for which his solution works.


In general though, on the topic of apply-templates, there is a larger issue that trips me up. Oftentimes, it seems that I mess up my set of templates in such a way that things get matched and copied to the output tree automatically, even though they are matched. To deal with this, I've been sticking a template at the top of my sheets that is:

<xsl:template match="text()"/>

Is this bad style?

Probably.


Is it a crappy hack to deal with messed up templates?

Yes (though I wouldn't have used exactly that choice of words :-), if your templates are messed up.


Or is it the correct way to suppress default rules?

Yes -- except that suppressing the default rules like this is not what you generally want to do.


I've seen this practice in stylesheets written by beginners who don't know how apply-templates work, and who then rely on value-of to get their output -- and who then discover they have to suppress the output of the default traversal (which this does). This can get really tangled and obscure: it works in simple cases, but doesn't scale. The real solution is to use apply-templates properly. In other words, don't suppress the default rules. Instead, use them -- in the understanding of what they do and why (which comes back to the processing model).

This accounts for what I like to call the "Taoist" character of XSLT. Think Yin and Yang. Most programmers are used to Yang processing (what in XSLT we call "pull"). Learn to rely on Yin. Use the Force, and the document will transform itself with minimal intervention from you.

I have situations where I have things along the lines of:

<node>
  <description>Foo</description>
  <datum>1</datum>
  <datum>2</datum>
</node>

When I do something like:

<xsl:template match="node">
  <h1><xsl:value-of select="description"/></h1>
  <ul><xsl:apply-templates/><ul>
</xsl:template>

<xsl:template match="node/datum">
 <li><xsl:value-of select="."/></li>
</xsl:template>

I end up with:

<h1>Foo</h1>
Foo  <!-- Extraneous foo that I don't want -->
<ul>
 <li>1</>
 <li>2</2>
</ul>

Tacking a <xsl:template match="text()"/> seems to catch the 'Foo' and kill it, but why do I have to do this?

Ah: this is precisly a case of what I was just describing. :->


Instead of what you have, try:

<xsl:template match="node">
  <xsl:apply-templates select="description"/>
  <ul>
    <xsl:apply-templates select="datum"/>
  <ul>
</xsl:template>

<xsl:template match="node/description">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="node/datum">
  <li><xsl:apply-templates/></li>
</xsl:template>

This is, as David C would say, "the XSL Way".

Note if you didn't need that <ul>...</ul> wrapper around the datum elements (the "data" :-), you could simply say <xsl:apply-templates/> (no explicit selections) in the template matching "node", because unless you say otherwise, all child nodes get selected (in document order). Then your stylesheet would be truly dead simple.

To test this, try substituting the template matching "node" above with this, and see what happens:

<xsl:template match="node">
  <xsl:apply-templates/>
</xsl:template>

Does the <apply-templates/> in node automatically copy the text values of any child nodes that are not explicitly matched?

Not as such, but it has that effect because the default traversal eventually reaches the text node descendants, which match the built-in template, which copies them out.


This is because the built-in rule for elements also applies templates:

<xsl:template match="*">
  <xsl:apply-templates/>
</xsl:template>

with the consequence that any elements that are not explicitly matched by your templates will just silently "keep going", by asking the processor to continue on to their children.

(Note that this means you could delete the template matching "node" altogether and get exactly the same result as the little test I just suggested above.)

Is it better to have an additional template that specifically matches 'description' and does nothing?

Not necessary, if you code as I have done above. Map the h1 to the description, not to its parent.


Or to take the <h1><xsl:value-of select="description"/></h1> out and move it into this explicit template? Or should I be doing something more like <xsl:apply-templates select="datum"/>?

As you can see.


An aside: I wonder how many beginners have been thrown off because the first XSLT they see is the ubiquitous construct

<html>
  <head>
    <title><xsl:value-of select="/path/to/title"/></head>
  </head>
...
</html>

in html-generating stylesheets. The value-of is there specifically *because* in an HTML <title> you want to (must) suppress all tagging. But this is the exception, not the rule.

Enjoy!
Wendell


====================================================================== Wendell Piez mailto:wapiez@xxxxxxxxxxxxxxxx Mulberry Technologies, Inc. http://www.mulberrytech.com 17 West Jefferson Street Direct Phone: 301/315-9635 Suite 207 Phone: 301/315-9631 Rockville, MD 20850 Fax: 301/315-8285 ---------------------------------------------------------------------- Mulberry Technologies: A Consultancy Specializing in SGML and XML ======================================================================


Current Thread
Keywords