 |
| XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead. |
Welcome to the p2p.wrox.com Forums.
You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

February 24th, 2010, 06:01 PM
|
|
Authorized User
|
|
Join Date: Feb 2009
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Attributes - Output only if they exist
Hi Everyone,
How do you only output an attribute if it exists? For example, if you have a table and a row element that can contain 0-3 attributes:
<table>
<row backgroundcolor="#ccc">
<data>row one</data>
</row>
<row backgroundcolor="00f" text-align="center">
<data>row one</data>
</row>
<row backgroundcolor="00f" text-align="center" text-weight="bold">
<data>row one</data>
</row>
</table>
I know how to output the attributes, something like the following block (outputting an html table row element):
<xsl:for-each select="row">
<tr bgcolor="{@background-color}" align="{@text-align}" style="font-weight:{@text-weight}">
The problem is that this will output empty attributes if the attribute doesn't exist in the source xml, and the processor I'm using will throw warnings if there are empty attributes. I've used <choose> blocks to test if an attribute exists and then outputting it if it does exist, but this can become really messy with nested <choose> blocks if there are many attributes.
So my question is, how would experienced xslt users approach this problem? How would you only output an attribute if it exists? Is there a simple check that one can do other than nesting a bunch of <choose> blocks?
Thanks,
Josh
|
|

February 24th, 2010, 07:26 PM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
Good question.
If the attribute name and value are the same in the input and output, I would use <xsl:copy-of select="@x"/>, which outputs nothing if @x is absent.
If the name or value are different, I would probably use <xsl:apply-templates select="@*"/>, with template rules like
Code:
<xsl:template match="@background-color">
<xsl:attribute name="bgcolor" select="."/>
</xsl:template>
(the select attribute of xsl:attribute is 2.0 only: in 1.0, use a nested xsl:value-of).
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
|
|

February 25th, 2010, 12:18 PM
|
|
Authorized User
|
|
Join Date: Feb 2009
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
In my project the attribute names and values are the same in the input and output, so I could use <xsl:copy-of select="@*"/>. I'm not clear how to use the xsl:copy-of element properly, however. How do I get the values that it grabs into my <tr> element? If I use the same for-each block as in my example, where do I put the copy-of, and how do I get the values into the <tr> element? I'm thinking something like the following, but I'm still unsure how to get the values into the <tr>:
<xsl:for-each select="row">
<xsl:copy-of select="@*"/>
<tr ???>
Thanks,
Josh
|
|

February 25th, 2010, 12:22 PM
|
 |
Friend of Wrox
|
|
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
|
|
Code:
<xsl:for-each select="row">
<tr>
<xsl:copy-of select="@*"/>
</tr>
</xsl:for-each>
|
|

February 25th, 2010, 02:16 PM
|
|
Authorized User
|
|
Join Date: Feb 2009
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Thanks for the reply. This works great, and simplifies my code significantly. I'm not sure I understand how this is working--I guess I never expected the xsl:copy-of statement to insert the attributes in the previous <tr> element. If anyone cares to explain the logic behind this I would love to hear it.
At any rate, thanks for the help. This was exactly what I was looking for.
Thanks,
Josh
|
|

February 25th, 2010, 03:05 PM
|
|
Friend of Wrox
|
|
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
|
|
Quote:
Originally Posted by JoshC
Thanks for the reply. This works great, and simplifies my code significantly. I'm not sure I understand how this is working--I guess I never expected the xsl:copy-of statement to insert the attributes in the previous <tr> element. If anyone cares to explain the logic behind this I would love to hear it.
|
The xsl:copy-of is a child of that literal result 'tr' element and that way it adds nodes to that 'tr' result element, in the case of xsl:copy-of select="@*" it adds attribute nodes to the 'tr' element.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
|
|

February 25th, 2010, 08:14 PM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
For the semantics of this, see the section "Constructing Complex Content" in the XSLT 2.0 spec. Within a literal result element like <tr>...</tr>, there's a sequence of instructions (called a "sequence constructor"). These instructions are evaluated to create (usually) a sequence of nodes, typically some attribute nodes and then some elements and/or text nodes. The attributes become attributes of the new (tr) element, the elements become its children. You can also of course create the attributes using logic such as
Code:
<tr>
<xsl:if test="condition">
<xsl:attribute name="valign" select="'top'"/>
</xsl:if>
<td>...</td>
</tr>
but in this case xsl:copy-of is easier.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
|
|
 |