Tintin81
Tintin81

Reputation: 10207

How to modify an XML attribute with XSLT?

I wonder if there's a way in XSLT to modify / add to an attribute value.

Right now I am simply replacing the attribute value:

<a class="project" href="#">
  <xsl:if test="new = 'Yes'">
    <xsl:attribute name="class">project new</xsl:attribute>
  </xsl:if>
</a>

But I don't like the repetition of project in line 2. Is there a better way to do this, e.g. to simply add new at the end of the attribute?

Thanks for any help!

Upvotes: 2

Views: 191

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243459

In XSLT 1.0 one can use this one-liner:

<a class="project{substring(' new', 5 - 4*(new = 'Yes'))}"/>

Here is a complete transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <a class="project{substring(' new', 5 - 4*(new = 'Yes'))}"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<t>
 <new>Yes</new>
</t>

the wanted, correct result is produced:

<a class="project new"/>

Explanation:

  1. Use of AVT (Attribute Value Templates)

  2. To select a string based on condition, in XPath 1.0 one can use the substring function and specify as the starting-index argument an expression, which evaluates to 1 when the condition is true() and to some number that is greater than the length of the string -- otherwize.

  3. We use the fact that in XPath 1.0 any argument of the * (multiplication) operator is converted to a number, and that number(true()) = 1 and number(false()) = 0


II. XSLT 2.0 solution:

Use this one-liner:

  <a class="project{(' new', '')[current()/new = 'Yes']}"/>

Here is a complete transformation:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <a class="project{(' new', '')[current()/new = 'Yes']}"/>
 </xsl:template>
</xsl:stylesheet>

When applied on the same XML document (above), again the same wanted, correct result is produced:

<a class="project new"/>

Explanation:

  1. Proper use of AVT.

  2. Proper use of sequences.

  3. Proper use of XSLT the current() function.

Upvotes: 0

Ian Roberts
Ian Roberts

Reputation: 122364

You can put the if inside the attribute instead of the other way round:

<a href="#">
  <xsl:attribute name="class">
    <xsl:text>project</xsl:text>
    <xsl:if test="new = 'Yes'">
      <xsl:text> new</xsl:text>
    </xsl:if>
  </xsl:attribute>
</a>

An <xsl:attribute> can contain any valid XSLT template (including for-each loops, applying other templates, etc.), the only restriction being that instantiating this template must only generate text nodes, not elements, attributes, etc. The attribute value will be the concatenation of all these text nodes.

Upvotes: 3

Related Questions