views:

38

answers:

3

Is there any way to optimize this code.

<xsl:choose>
    <xsl:when test="val1 = val2">
        <xsl:apply-templates select="node"/>
    </xsl:when>
    <xsl:otherwise>
        <div>
            <xsl:apply-templates select="node"/>
        </div>
    </xsl:otherwise>
</xsl:choose>

I do not like having to write twice the same <xsl:apply-templates select="node"/>.


Update:

The idea is that depending on the result of comparison to do one of two things:

  1. Just print some information (which we obtain after applying the template <xsl:apply-templates select="node"/>).

  2. Print the same information, but in advance "wrapping" it in the container (div for example).

A: 

The xslt you are posting here is not very optimizable. If the code that you don't want to duplicate would be more than two lines, you could optimize by creating a named template and calling that instead. In this example it really doesn't make much sense, but you'll get the idea:

<xsl:when test="val1=val2">
  <xsl:call-template name="optimized"/>
</xsl:when>
<xsl:otherwise>
  <div>
    <xsl:call-template name="optimized"/>
  </div>
</xsl:otherwise>

And the template:

<xsl:template name="optimized">
  <xsl:apply-templates select="node"/>
</xsl:template>

The called template is in the same context as the calling code, so you can just copy the code you don't want to duplicate to the named template.

Jan Willem B
A: 

Use:

<xsl:apply-templates select="node">
  <xsl:sort select="not(val1 = val2)"/>
</xsl:apply-templates>

Here is a complete example. This transformation:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

 <xsl:template match="/*">
  <t>
        <xsl:apply-templates select="node">
          <xsl:sort select="not(val1 = val2)"/>
        </xsl:apply-templates>
  </t>
 </xsl:template>

 <xsl:template match="node[not(val1 = val2)]">
  <div>
     <node>
       <xsl:apply-templates/>
     </node>
  </div>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<t>
  <node>
    <val1>1</val1>
    <val2>2</val2>
  </node>
  <node>
    <val1>3</val1>
    <val2>3</val2>
  </node>
</t>

produces the wanted, correct result:

<t>
   <node>
      <val1>3</val1>
      <val2>3</val2>
   </node>
   <div>
      <node>
         <val1>1</val1>
         <val2>2</val2>
      </node>
   </div>
</t>

Explanation of the solution:

Whenever an <xsl:apply-templates> has an <xsl:sort> child, the nodes that are selected are sorted according the data provided in the <xsl:sort> child(ren) and the results of applying templates on each selected node appear in the output in that (sort) order -- not in document order.

In the transformation above we have:

<xsl:apply-templates select="node"> 
  <xsl:sort select="not(val1 = val2)"/> 
</xsl:apply-templates> 

This means that the results of applying templates to the elements named node for which it is true that val1=val2 will appear before the results of applying templates to the elements named node for which val1=val2 is not true. This is because false sorts before true.

If this explanation is not clear, the reader is directed to read more about <xsl:sort>.

Dimitre Novatchev
@Dimitre Novatchev, I do not understand what's going on here sorting.
Kalinin
@Kalinin: I updated my answer with an explanation.
Dimitre Novatchev
A: 

This is hardy possible for that simple example, but if the amount of the wrapping code and the duplicate code are worth bothering, you have two options:

  1. introduce a chain of templates with different modes:

    <!-- instead of choose -->
    <xsl:apply-template match="." mode="container" />
    ...
    <xsl:template match="container" mode="container">
        <xsl:apply-templates select="." mode="dup-group"/>
    </xsl:template>
    
    
    <xsl:template match="container[val1=val2]" mode="container">
        <div>
            <xsl:apply-templates select="." mode="dup-group"/>
        </div>
    </xsl:template>
    
    
    <xsl:template match="container" mode="dup-group">
        <xsl:apply-templates select="node"/>
        <!-- other duplicate code -->
        ...
    </xsl:template>
    
  2. move a part of code into a separate imported stylesheet and override the template rule in the current stylesheet:

    <xsl:template match="container[val1=val2]">
        <div>
            <xsl:apply-imports />
        </div>
    </xsl:template>
    
newtover