[prev in list] [next in list] [prev in thread] [next in thread] 

List:       xsl-list
Subject:    Re: [xsl] Copying nested tags and adding attributes
From:       Abel Braaksma <abel.online () xs4all ! nl>
Date:       2007-01-30 9:31:22
Message-ID: 45BF106A.8030702 () xs4all ! nl
[Download RAW message or body]

graeme.ludwig@bt.com wrote:
> <description>See how <b>cans</b> are made into model cars, planes, bikes, etc. at  \
> <a href="http://www.tincanmodels.com/" target="_self">Tin Can Models</a>. \
> </description> 
> and transform it into
> 
> See how <b>cans</b> are made into model cars, planes, bikes, etc. at  <a \
> href="http://www.tincanmodels.com/" alt="Tin Can Models" target="_self">Tin Can \
> Models</a>. 
> i.e. I want to add an alt attribute to the hyperlink and set it to the text of the \
> link itself. The bold attribute should remain unchanged. 
> <xsl:template match="description">
> <xsl:copy-of select="node()"/>
> </xsl:template>
> 
('bold' is not an attribute, but a node)

If you want to do a copy and you want to change some things, the best 
method to use is the identity template, or copying template. Sel Mangano 
in his "XSLT Cookbook" calls this technique the "Overriding Copy Idiom" 
(chapter "XML to XML").

The trick is simple, but when beginning with XSLT a little confusing: 
you create a "rule" (in xslt terms: a template) that makes a shallow 
copy of the current node and applies all templates for all children 
nodes. In XSLT, only the most specific rule is used, which makes it easy 
to override (that's why 'Overriding Copy Idiom') this general rule with 
more specific ones.

The general template looks like this:

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

This template has been thoroughly discussed several times in the 
archive, so I leave it to this for now. Note the difference between 
"copy-of" and "copy". The first copies everything, including all 
children, and *does not* let you override anything. The second only 
copies the current node and gives you the option to do whatever you want 
with it (like adding children like attributes, texts, other nodes whatever).

After you have added the above template, all you need to do is adding 
specific templates. In your case, you seem to want to remove the 
'description' node (well, the tag, as you may put it) but you want to 
keep its children:

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

Run it with only these two and watch the effect. Note the absence of 
"xsl:copy".
Next, you'd like to change the 'a'-node, in particular, you want to add 
an attribute 'alt' to the 'a'-node. This can be done in several ways, I 
show you one:

<xsl:template match="a">
    <xsl:copy>
        <xsl:attribute name="alt"><xsl:value-of select="." 
/></xsl:attribute>
        <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
</xsl:template>

Run it, with only this and the copy template and watch the effect. Its 
intent is to copy itself ('a'), add an attribute, and then add the 
children (the text, the attributes). Note that it will give a dynamic 
error if you place xsl:attribute after the xsl:apply-templates. This is 
because attributes must be created before any children of a node are 
created (the child is here: the text of the 'a' node).

If you place all templates inside a stylesheet (order is not important), 
and run it with your source, you will get the desired result, because 
the XSLT processor will automatically choose the matching templates for 
'a' and 'description' when it encounters them in the source tree.



> 
> to get the text out as per solution 12, but am unsure how to generate the extra alt \
> attribute for any <a> elements that occur within the text. I believe I am using an \
> XSLT 1.0 processor. 

If you are unsure, run the following stylesheet, it will print the 
version for you (also see the List Guidelines at MulberryTech's):

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
    <xsl:output method="text"/>

    <xsl:template match="/">
        <xsl:text>My XSLT Version: </xsl:text>
        <xsl:value-of select="system-property('xsl:version')"/>
    </xsl:template>
   
</xsl:stylesheet>


The output will look like:

My XSLT Version: 1.0



Cheers,
-- Abel Braaksma
   http://www.nuntia.nl

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe@lists.mulberrytech.com>
--~--


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic