RE: [xsl] deep "copy-of" a source fragment

Subject: RE: [xsl] deep "copy-of" a source fragment
From: "Dion Houston" <dionh@xxxxxxxxxxxxx>
Date: Wed, 4 Sep 2002 00:44:03 -0700
Hi Terence!

Welcome to the list!

This was a highly tricky one, and since you didn't give us a sample of
your XML it took me a while to figure it out...  The problem is not what
you think it is, though :)

Before giving you the answer, the first thing that puzzled me was how it
could copy just text nodes...  With <xsl:value-of> this is extremely
easy, but not so with <xsl:copy-of> (for sake of trivia, <xsl:copy-of
select="text()"/> should do the trick).  The other question was how
changing your XPath expression didn't change anything...

Peter (below) hit the first issue perfectly.  The problem is that your
xsl:copy-of ---WASN'T DOING ANYTHING---!  Changing the match expression
was causing the default template rule for text to run which was copying
your text nodes.  This was giving the illusion of your statement copying
just the text.

The bigger question is WHY wasn't the copy-of doing anything.  This was
actually trickier, and I haven't seen the answer posted yet.  If you
take a look at HTML Tidy's XHTML output, you should see the following
line:

<html xmlns="http://www.w3.org/1999/xhtml";>

The key here (as dozens of people will tell you) is that a namespace is
in use, even though it is the default.  This causes every XPath
expression without a prefix to fail (this is a FAQ).  To solve this,
simply change your XSLT to:

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
     xmlns:x="http://www.w3.org/1999/xhtml";>

     <xsl:template match="x:body">
         <xsl:copy-of select="node()"/> <!-- copies the children
exclusive of body -->
     </xsl:template>

     <!-- change the default text rule to not output text -->
     <xsl:template match="text()"/>
</xsl:stylesheet>

Thanks for the puzzler, and HTH!

Dion
-----Original Message-----
From: Peter Davis [mailto:pdavis152@xxxxxxxxx] 
Sent: Wednesday, September 04, 2002 12:06 AM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re: [xsl] deep "copy-of" a source fragment

On Tuesday 03 September 2002 23:48, Terence Kearns wrote:
> but if I change
> 	match="/"
> to
> 	match="/html/body"
>
> then I get leaf nodes again :(
> I also tried
...
> makes no difference if I change
>
> 	select="node()"
> to
> 	select="."
> or
> 	select="@*|node()"
>
> Is there no way at all to copy a fragment from the source tree onto
the
> result tree?!

The problem is, if you have:

<xsl:template match="/html/body">
  ...
</xsl:template>

then the default template will be applied to all the nodes that aren't
part of 
/html/body.  The default template is to copy text nodes and ignore
everything 
else, so that might explain your problem.

Instead of changing select to select="node()" and match="/html/body",
you 
should try,

<xsl:template match="/">
  <xsl:copy-of select="html/body/node()"/>
</xsl:template>

If that doesn't work, then I don't know what's wrong.

An alternative to using copy-of is to use the "identity template", which
can 
look something like this (untested):

<!-- ignore all nodes unless otherwise specified -->
<xsl:template match="node()" priority="0"/>

<xsl:template match="/html/body//@* | /html/body//node()">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

You might want to go this route if you ever want to apply other
templates to 
specific elements in the /html/body//* tree.  Using xsl:copy-of doesn't
allow 
you to modify the output of the copy with other templates.  If you don't

think you'll ever need to apply other templates, then copy-of should
work 
fine.

-- 
Peter Davis

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


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


Current Thread