Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT conditions / variable scope

Tags:

scope

xslt

<xsl:choose>
    <xsl:when test="long convoluted expression">
        <xsl:element name="Class">123</xsl:element>
        <a lot more xsl:elements>
    </xsl:when>
    <xsl:when test="next very long expression">
        <xml:element name="Class">124</xsl:element>
        <a lot more xsl:elements>
    </xsl:when>
    <tens of more similar xsl:when>
</xsl:choose>

Is there a way to simplify the above code with conditionals? For every class value the objects are given, there follows tens of rows with extra attributes. These attributes form sets according to the value of the class. Class 0-99 has one set of extra tags, class 100-199 a second, forming a maintenance nightmare when one of these extra tag sets change.

I was considering a solution like this:

<xsl:choose>
    <xsl:when test="long convoluted expression">
        <xml:element name="Class">123</xsl:element>
        <xsl:variable name="outputclass" select="123">
    </xml:when>
    <xsl:when test="next very long expression">
        <xml:element name="Class">124</xsl:element>
        <xsl:variable name="outputclass" select="124">
    </xsl:when>
</xsl:choose>
<xsl:choose>
    <xsl:when test="$outputclass > 99">
        <xml:elements for classes 100-199 here>
    </xsl:when>
<xsl:choose>

But of course this fails, as the outputclass variable is not in the same scope. Any way to go around this?

like image 847
diskis Avatar asked Jul 02 '10 12:07

diskis


2 Answers

The best solution to this problem is well-known:

<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="/">
    <xsl:variable name="voutType">
      <xsl:choose>
        <xsl:when test="long convoluted expression">123</xsl:when>
        <xsl:when test="next very long expression">124</xsl:when>
        <!-- Etcetera ... -->
      </xsl:choose>
    </xsl:variable>

   <Class><xsl:value-of select="$voutType"</Class>
         <xsl:choose>
            <xsl:when test="not($voutType > 99)">
                <!-- elements for classes 0 - 99 here -->
            </xsl:when>
            <xsl:otherwise>
                <!-- elements for classes 100-199 here -->
            </xsl:otherwise>
         <xsl:choose>
 </xsl:template>
</xsl:stylesheet>

Do note:

  1. In order to give values to a variable ($voutType), the <xsl:choose> instruction must be inside the body of the <xsl:variable>

  2. You only need to specify the <Class> element once -- outside of everything else.

  3. You don't have to use <xsl:element> if the element name is known.

like image 113
Dimitre Novatchev Avatar answered Oct 05 '22 22:10

Dimitre Novatchev


If you're trying to make it easy to specify a bunch of attributes (not tags aka elements!), then it sounds to me like attribute sets are what you need, and you may not need variables:

http://www.w3.org/TR/xslt#attribute-sets

That assumes that the attribute values themselves aren't dependent on the class's value; only their presence is.

If elements are really what you mean, try using named templates with an input parameter. At the top level of your stylesheet:

<xsl:template name="classdef">
  <xsl:param name="classid"/>
  <!-- Note: I put the class elem in here so I don't have to
        write individual class ids more than once -->
  <xsl:element name="Class"><xsl:value-of select="$classid"/></xsl:element>
  <xsl:choose>
    <xsl:when test="$classid > 99">
      ...
    </xsl:when>
  </xsl:choose>
</xsl:template>

and in your other template:

<xsl:call-template name="classdef">
  <xsl:with-param name="classid">124</xsl:with-param>
</xsl:call-template>

See http://www.w3.org/TR/xslt#variables for more info on params.

like image 25
Owen S. Avatar answered Oct 05 '22 22:10

Owen S.