Home > database >  Complex variable creation and usage in XSLT
Complex variable creation and usage in XSLT

Time:08-08

I have the following XML:

  <?xml version="1.0" encoding="UTF-8"?>
    <?xml-stylesheet type="text/xml?>
    <cars>
      <car model="Focus" manufacturer="Ford" year="2000"/>
      <car model="Golf" manufacturer="Volkswagen" year="1999"/>
      <car model="Camry" manufacturer="Toyota" year="1999"/>
      <car model="Civic" manufacturer="Honda" year="2000"/>
      <car model="Prizm" manufacturer="Chevrolet" year="2000"/>
    </cars>

I tried to apply the following XSLT:

    <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="html" version="1.0"/>

<xsl:variable name="bgcolor">
 <body>#cccccc</body>
 <table>#ddddd</table>
 <row>#eeeeee</row>
 <altrow>#ffffff</altrow>
</xsl:variable>

<xsl:template match="/">
 <html>
 <body bgcolor="{$bgcolor/body}">
  <xsl:apply-templates/>
 </body>
 </html>
</xsl:template>

<xsl:template match="cars">
 <table bgcolor="{$bgcolor/table}" width="75%">
  <xsl:for-each select="car">
   <tr>
    <xsl:attribute name="bgcolor">
     <xsl:choose>
      <xsl:when test="position() mod 2 = 0">
       <xsl:value-of select="$bgcolor/altrow"/>
      </xsl:when>
      <xsl:when test="position() mod 2 = 1">
       <xsl:value-of select="$bgcolor/row"/>
      </xsl:when>
     </xsl:choose>
    </xsl:attribute>
    <xsl:call-template name="car"/>
   </tr>
  </xsl:for-each>
 </table>
</xsl:template>

<xsl:template name="car">
 <td><xsl:value-of select="@model"/></td>
 <td><xsl:value-of select="@manufacturer"/></td>
 <td><xsl:value-of select="@year"/></td>
</xsl:template>

</xsl:stylesheet>

After applying the XSLT to XML I get:

<?xml version="1.0" encoding="utf-8" ?> 
    <html>
    <body bgcolor="#cccccc">
    <table bgcolor="#cccccc" width="75%">
    <tr bgcolor="#cccccc">
      <td>Focus</td> 
      <td>Ford</td> 
      <td>2000</td> 
      </tr>
    <tr bgcolor="#cccccc">
      <td>Golf</td> 
      <td>Volkswagen</td> 
      <td>1999</td> 
      </tr>
    <tr bgcolor="#cccccc">
      <td>Camry</td> 
      <td>Toyota</td> 
      <td>1999</td> 
      </tr>
    <tr bgcolor="#cccccc">
      <td>Civic</td> 
      <td>Honda</td> 
      <td>2000</td> 
      </tr>
    <tr bgcolor="#cccccc">
      <td>Prizm</td> 
      <td>Chevrolet</td> 
      <td>2000</td> 
      </tr>
      </table>
      </body>
      </html>

What I need to get is:

 <?xml version="1.0" encoding="utf-8" ?> 
    <html>
    <body bgcolor="#cccccc">
    <table bgcolor="#ddddd" width="75%">
    <tr bgcolor="#eeeeee">
      <td>Focus</td> 
      <td>Ford</td> 
      <td>2000</td> 
      </tr>
    <tr bgcolor="#ffffff">
      <td>Golf</td> 
      <td>Volkswagen</td> 
      <td>1999</td> 
      </tr>
    <tr bgcolor="#eeeeee">
      <td>Camry</td> 
      <td>Toyota</td> 
      <td>1999</td> 
      </tr>
    <tr bgcolor="#ffffff">
      <td>Civic</td> 
      <td>Honda</td> 
      <td>2000</td> 
      </tr>
    <tr bgcolor="#eeeeee">
      <td>Prizm</td> 
      <td>Chevrolet</td> 
      <td>2000</td> 
      </tr>
      </table>
      </body>
      </html>

It seems like the "complex" variable $bgcolor is using only the first value #cccccc. How can I change the code so that it uses the appropriate values on the selected elements? And I need to keep this $bgcolor variable as it is. Thank you!

CodePudding user response:

Not that I think this is a particularly good approach, but if you want, you can get your stylesheet to recognize your variable as a node-set by treating it as a literal element instead of a variable. This is an alternative to using an extension function which your processor may or may not support:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:variable name="bgcolor">
    <body>#cccccc</body>
    <table>#ddddd</table>
    <row>#eeeeee</row>
    <altrow>#ffffff</altrow>
</xsl:variable>

<xsl:variable name="bgcolors" select="document('')/xsl:stylesheet/xsl:variable[@name='bgcolor']" />

<xsl:template match="/">
    <html>
        <body bgcolor="{$bgcolors/body}">
            <xsl:apply-templates/>
        </body>
    </html>
</xsl:template>

<xsl:template match="cars">
    <table bgcolor="{$bgcolors/table}" width="75%">
        <xsl:for-each select="car">
            <tr>
                <xsl:attribute name="bgcolor">
                    <xsl:choose>
                        <xsl:when test="position() mod 2 = 1">
                            <xsl:value-of select="$bgcolors/row"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="$bgcolors/altrow"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:attribute>
                <td>
                    <xsl:value-of select="@model"/>
                </td>
                    <td><xsl:value-of select="@manufacturer"/>
                </td>
                <td>
                    <xsl:value-of select="@year"/>
                </td>
            </tr>
        </xsl:for-each>
    </table>
</xsl:template>

</xsl:stylesheet>

OTOH, your setup may not allow using the document() function to perform an internal lookup, so...


P.S. There is no HTML version 1.0. That too will throw an error with some processors.

CodePudding user response:

Using Saxon, I get an error because HTML version 1.0 is not supported. When I correct the xsl:output declaration, I get the output you are expecting.

With a conformant XSLT 1.0 processor I would expect an error because you are using a result-tree-fragment as a node-set, as Martin Honnen explains.

I can't think of any explanation for the output you say you are getting: I think it's a problem particular to your XSLT processor.

  • Related