views:

42

answers:

3
<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?

+1  A: 

I've only done XSLT a few times, but each time it seems like a huge maintenance nightmare. At any rate, I think you could set a variable saying what the class is, and then call a function as xsl:template match with the class as the arg. You would declare that function as xsl:template name, because you don't want to match on it automatically. Inside the function you should be able to script out those 100 values. Not sure whether this helps, but it organizes the code in a way that might.

MJB
Yes, thank you. Quick testing shows that this will work perfectly for my purposes. Now only to brush up on the xml:with-param syntax, and I'll do fine with a few less templates.
diskis
+1  A: 

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.

Owen S.
Thank you, this is essentially the same solution as MJB posted, and will do perfectly for my needs. I'll come back and give you an upvote when i get enough reputation to do it :)
diskis
+2  A: 

The best solution to this problem is well-known:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <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.

Dimitre Novatchev
This is a beautiful solution. As easy to maintain as the other solutions, but even shorter. That you can nest choose within a variable I did not know. Thank you!
diskis
@diskis: I'm seriously glad that you un-accepted my answer. It did not seem sufficient, but I thought it might get you going in the right direction. This definitely seems to be the better way.
MJB