8

My XML file is as follows:

<worksheet>
<row>
<rowTitle>RT1</rowTitle>
<rowType>yesorno</rowType>
<subLine>subLine1Content</subLine>
<subLine>subLine2Content</subLine>
</row>
<row>
<rowTitle>RT2</rowTitle>
<rowType>operation</rowType>
<subLine>subLine1Content</subLine>
<subLine>subLine2Content</subLine>
<subLine>subLine3Content</subLine>
</row>
.
.
</worksheet>

in my xsl, while displaying contents of a particular row, i'd like to add a class to the html element that'll specify the type of the row it is. I tried using xsl:choose and assigning value to a xsl:variable, but that doesn't work. I'm trying to display the row as

<ol>
<li class="rowTypeBoolean">
RT1
<ul><li>subLineContent1</li>
<li>subLineContent2</li></ul>
</li>
<li class="rowTypeOptions">
RT2
<ul><li>subLineContent1</li>
<li>subLineContent2</li>
<li>subLineContent3</li></ul>
</li>
.    
.
</ol>

XSL file snippet

<xsl:template match="row">
        <li class="rowClass ${className}">
            <xsl:choose>
                <xsl:when test="type = 'YESORNO'">
                    <xsl:variable name="className" select="rowTypeBoolean"/>
                </xsl:when>
                <xsl:when test="type = 'OPTIONS'">
                    <xsl:variable name="className" select="rowTypeOptions"/>
                </xsl:when>
                <xsl:when test="type = 'OPERATION'">
                     <xsl:variable name="className" select="rowTypeOperation"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:variable name="className" select="rowTypeOther"/>
                </xsl:otherwise>
            </xsl:choose>
            <span class="rowTitleClass">
                <xsl:value-of select="rowtitle"/>
            </span>
            <br/>
            <ul class="subLineListClass">
                <xsl:apply-templates select="subLine"/>
            </ul>
        </li>
</xsl:template>
2
  • You have posted your XML and your desired output. Can you post the XSL you are currently having problems with? Commented Aug 9, 2010 at 19:05
  • Good question (+1). See my answer for a complete solution that is simple and avoids unnecessary coding of decision-taking logic. Commented Aug 9, 2010 at 20:17

3 Answers 3

10

You need to add it as an attribute to the element:

    <li>
        <xsl:choose>
            <xsl:when test="type = 'YESORNO'">
                <xsl:attribute name="className">rowTypeBoolean</xsl:attribute>
            </xsl:when>
            <xsl:when test="type = 'OPTIONS'">
                <xsl:attribute name="className">rowTypeOptions</xsl:attribute>
            </xsl:when>
            <xsl:when test="type = 'OPERATION'">
                <xsl:attribute name="className">rowTypeOperation"</xsl:attribute>
            </xsl:when>
            <xsl:otherwise>
                <xsl:attribute name="className">rowTypeOther"</xsl:attribute>
            </xsl:otherwise>
        </xsl:choose>
    </li>
Sign up to request clarification or add additional context in comments.

Comments

6

The most naive solution would be to use xsl:choose instruction like this:

<li>
    <xsl:attribute name="className"> 
        <xsl:choose> 
            <xsl:when test="type = 'YESORNO'">rowTypeBoolean</xsl:when> 
            <xsl:when test="type = 'OPTIONS'">rowTypeOptions</xsl:when> 
            <xsl:when test="type = 'OPERATION'">rowTypeOperation</xsl:when> 
            <xsl:otherwise>rowTypeOther</xsl:otherwise> 
        </xsl:choose> 
    </xsl:attribute>
</li> 

Other way would be to have an inline map (or via fn:document()) like:

<li class="{$map[@type = current()/type]|$map[not(@type)]}"/>

With this as top level element

<map:map xmlns:map="map">
    <item type="YESORNO">rowTypeBoolean</item>
    <item type="OPTIONS">rowTypeOption</item>
    <item type="OPERATIONS">rowTypeOperation</item>
    <item>rowTypeOther</item>
</map:map>

<xsl:variable name="map" select="document('')/*/map:map/*" xmlns:map="map"/>

Comments

4

It isn't necessary at all to use <xsl:choose> in the transformation:

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <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="worksheet">
  <ol>
   <xsl:apply-templates/>
  </ol>
 </xsl:template>

 <xsl:template match="row[rowType='yesorno']">
   <li class="rowTypeBoolean">
     <xsl:apply-templates/>
   </li>
 </xsl:template>

 <xsl:template match="row[rowType='operation']">
   <li class="rowTypeOperation">
     <xsl:apply-templates/>
   </li>
 </xsl:template>

 <xsl:template match="row[rowType='options']">
   <li class="rowTypeOptions">
     <xsl:apply-templates/>
   </li>
 </xsl:template>

 <xsl:template match="row">
   <li class="rowTypeOther">
     <xsl:apply-templates/>
   </li>
 </xsl:template>

 <xsl:template match="subLine[1]">
  <ul>
    <xsl:apply-templates select="../subLine" mode="process"/>
  </ul>
 </xsl:template>

  <xsl:template match="subLine" mode="process">
    <li><xsl:apply-templates/></li>
  </xsl:template>

</xsl:stylesheet>

when applied on the provided XML document:

<worksheet>
    <row>
        <rowTitle>RT1</rowTitle>
        <rowType>yesorno</rowType>
        <subLine>subLine1Content</subLine>
        <subLine>subLine2Content</subLine>
    </row>
    <row>
        <rowTitle>RT2</rowTitle>
        <rowType>operation</rowType>
        <subLine>subLine1Content</subLine>
        <subLine>subLine2Content</subLine>
        <subLine>subLine3Content</subLine>
    </row>
</worksheet>

produces the wanted, correct result:

<ol>
    <li class="rowTypeBoolean">
        <rowTitle>RT1</rowTitle>
        <rowType>yesorno</rowType>
        <ul>
            <li>subLine1Content</li>
            <li>subLine2Content</li>
        </ul>
        <subLine>subLine2Content</subLine>
    </li>
    <li class="rowTypeOperation">
        <rowTitle>RT2</rowTitle>
        <rowType>operation</rowType>
        <ul>
            <li>subLine1Content</li>
            <li>subLine2Content</li>
            <li>subLine3Content</li>
        </ul>
        <subLine>subLine2Content</subLine>
        <subLine>subLine3Content</subLine>
    </li>
</ol>

Do note:

  1. Only simple templates and <xsl:apply-templates> are used. Therefore, the chances of committing an error are minimized.

  2. This code is inherently extensible and maintainable. If a new row type is introduced none of the existing templates will need to be altered -- just a new short and simple template will need to be added, that matches a row element having a rowType child with the new value.

  3. The mapping between rowType and the corresponding value of the class attribute can be specified in a special global-level namespaced element and/or variable (as done in Alejandro's solution), or even in a separate XML document. Then we can have just one single template, matching row.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.