XSLT optional attributes

E

Eric Anderson

I'm not sure if this is the correct place to post this question so
please feel free to redirect me if needed.

I am writing a XSL transform document to perform a transform on an XML
document. I want some of my XML tags to have optional attributes. If the
person does not specify the value of an attribute I will assume a
default value. What is the best way to do this in XSL. Let me give an
example to make my problem clear:

foo.xml
-------

<root>
<tag attr="value"/>
<tag attr="another_value"/>
</root>

bar.xsl
-------
<xsl:template match="/root/tag">
Attribute 'attr' is <xsl:value-of select="@attr"/>
</xsl:template>

output
------
Attribute 'attr' is value
Attribute 'attr' is another_value

Now lets make one change to the above transforms. We want the attribute
'attr' to default to the value of "default" if not specified. Now one
way to do this would be:

bar.xsl
-------
<xsl:template match="/root/tag">
<xsl:choose>
<xsl:when test="@attr = ''">
Attribute 'attr' is default
</xsl:when>
<xsl:eek:therwise>
Attribute 'attr' is <xsl:value-of select="@attr"/>
</xsl:eek:therwise>
<xsl:choose>
</xsl:template>

The solution above is not really scalable. What if you want to have lots
of attributes that have defaults? And what if you are doing more than
printing a sentence out? Do you really want to repeat hundreds of lines
with just a few small changes between the different blocks of code? A
better method is if we could at the top of our template simply check if
the value is set. If it is not set then we would want to be able to set
it. My first thought was to use <xsl:variable> tag like below:

<xsl:template match="/root/tag">
<xsl:choose>
<xsl:when test="@attr = ''">
<xsl:variable name="attr" select="'default'"/>
<xsl:when>
<xsl:eek:therwise>
<xsl:variable name="attr" select="@attr"/>
<xsl:eek:therwise>
</xsl:choose>
Attribute 'attr' is <xsl:value-of select="$attr"/>
</xsl:template>

The problem is that the variable 'attr' seems to only be defined in the
context of the conditional (at least it seems to be that way in
Mozilla's XSLT processing). Once the conditional has ended (xsl:choose)
then it is no longer defined. I tried to define an empty variable 'attr'
prior to the conditional to indicate it should be defined in the scope
of the template tag not the conditional tags, but that violate the rule
of not being allowed to change a the value of a variable (by the way,
who was the idiot that thought it was a good idea to have a tag which
defines constants and call it a variable! :) ).

So does anyone have any good ideas on how I would get around this? What
is the right way to do this in XSLT?

Thanks for your suggestions,

Eric
 
P

Peter Gerstbach

Hi Eric,

you are right, the scope of xsl:variable ist only the current element.
But you can place the xsl:when inside the xsl:variable

<xsl:variable name="attr">
<xsl:when test="@attr = ''">
<xsl:value-of select="'default'"/>
</xsl:when>
<xsl:eek:therwise>
<xsl:value-of select="@attr"/>
<xsl:eek:therwise>
</xsl:variable>

then it should be possible to reference to $attr afterwards in the
template. I did not tried it but I hope it works!

Peter

Eric Anderson wrote:
....
 
R

Richard Tobin

Eric Anderson said:
<xsl:template match="/root/tag">
<xsl:choose>
<xsl:when test="@attr = ''">
Attribute 'attr' is default
</xsl:when>
<xsl:eek:therwise>
Attribute 'attr' is <xsl:value-of select="@attr"/>
</xsl:eek:therwise>
<xsl:choose>
</xsl:template>

You might consider a different approach:

<xsl:template match="/root/tag">
Attribute 'attr' is <xsl:apply-templates mode="attr-default" select="."/>
</xsl:template>

<xsl:template mode="attr-default" match="*[@attr]">
<xsl:value-of select="@attr"/>
</xsl:template>

<xsl:template mode="attr-default" match="*">
<xsl:text>default</xsl:text>
</xsl:template>

Or you could use a named template containing <xsl:choose>.

-- Richard
 
E

Eric Anderson

Richard said:
You might consider a different approach:

<xsl:template match="/root/tag">
Attribute 'attr' is <xsl:apply-templates mode="attr-default" select="."/>
</xsl:template>

<xsl:template mode="attr-default" match="*[@attr]">
<xsl:value-of select="@attr"/>
</xsl:template>

<xsl:template mode="attr-default" match="*">
<xsl:text>default</xsl:text>
</xsl:template>

Or you could use a named template containing <xsl:choose>.

I thought about these methods, but both have the same problem as my
first suggestion. It doesn't scale well. What if I have a tag that has
10 attributes and the tag has lots of complex code.

Thanks for the suggestion anyway,

Eric
 
E

Eric Anderson

Peter said:
you are right, the scope of xsl:variable ist only the current element.
But you can place the xsl:when inside the xsl:variable

<xsl:variable name="attr">
<xsl:when test="@attr = ''">
<xsl:value-of select="'default'"/>
</xsl:when>
<xsl:eek:therwise>
<xsl:value-of select="@attr"/>
<xsl:eek:therwise>
</xsl:variable>

then it should be possible to reference to $attr afterwards in the
template. I did not tried it but I hope it works!

I haven't tried it yet, but I am sure this method works and it is
exactly what I was looking for. I haven't been a XSLT hacker very long
so I am still getting used to it's funky way of doing things. I guess it
is not much different than C-based languages having the following construct:

_attr = attr ? bar : "default";

Thanks for your help,

Eric
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top