2
votes

I'm relatively new to XSLT and am wondering if there are alternate (and possibly more efficient) ways to control the output of one set/type of XML nodes based on another set of nodes in the same document.

For example, given the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<table>
    <titles>
        <title1>Header Column One</title1>
        <title2/>
        <title3>Header Column Three</title3>
        <title4/>
        <title5/>
    </titles>
    <valueRows>
        <valueRow>
            <value1>Data Row1 Value1</value1>
            <value2>Data Row1 Value2</value2>
            <value3>Data Row1 Value3</value3>
            <value4>Data Row1 Value4</value4>
            <value5/>
        </valueRow>
        <valueRow>
            <value1>Data Row2 Value1</value1>
            <value2>Data Row2 Value2</value2>
            <value3>Data Row2 Value3</value3>
            <value4>Data Row2 Value4</value4>
            <value5/>
        </valueRow>
    </valueRows>
</table>

I want to use the /table/titles/title# nodes to determine which /table/valueRows/valueRow/value# items to display. Specifically, if a title has data, the corresponding values should be displayed. If the title is empty, the respective values should not be displayed. Even if it they have content.

Based on the above example, the output would be:

<table>
    <tr>
        <th>Header Column One</th>
        <th>Header Column Three</th>
    </tr>
    <tr>
        <td>Data Row1 Value1</td>
        <td>Data Row1 Value3</td>
    </tr>
    <tr>
        <td>Data Row2 Value1</td>
        <td>Data Row2 Value3</td>
    </tr>
</table>

The XSLT I've come up with to produce that output is as follows:

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

    <xsl:output method="html"/>

    <xsl:template match="/">

        <table>
            <tr>

                <xsl:if test="/table/titles/title1 != ''">
                    <th>
                        <xsl:value-of select="/table/titles/title1"/>
                    </th>
                </xsl:if>

                <xsl:if test="/table/titles/title2 != ''">
                    <th>
                        <xsl:value-of select="/table/titles/title2"/>
                    </th>
                </xsl:if>

                <xsl:if test="/table/titles/title3 != ''">
                    <th>
                        <xsl:value-of select="/table/titles/title3"/>
                    </th>
                </xsl:if>

                <xsl:if test="/table/titles/title4 != ''">
                    <th>
                        <xsl:value-of select="/table/titles/title4"/>
                    </th>
                </xsl:if>

                <xsl:if test="/table/titles/title5 != ''">
                    <th>
                        <xsl:value-of select="/table/titles/title5"/>
                    </th>
                </xsl:if>

            </tr>

            <xsl:for-each select="/table/valueRows/valueRow">
                <tr>

                    <xsl:if test="/table/titles/title1 != ''">
                        <td>
                            <xsl:value-of select="value1"/>
                        </td>
                    </xsl:if>

                    <xsl:if test="/table/titles/title2 != ''">
                        <td>
                            <xsl:value-of select="value2"/>
                        </td>
                    </xsl:if>

                    <xsl:if test="/table/titles/title3 != ''">
                        <td>
                            <xsl:value-of select="value3"/>
                        </td>
                    </xsl:if>

                    <xsl:if test="/table/titles/title4 != ''">
                        <td>
                            <xsl:value-of select="value4"/>
                        </td>
                    </xsl:if>

                    <xsl:if test="/table/titles/title5 != ''">
                        <td>
                            <xsl:value-of select="value5"/>
                        </td>
                    </xsl:if>

                </tr>
            </xsl:for-each>

        </table>
    </xsl:template>

</xsl:stylesheet>

To help in learning the ins and outs of XSLT, my question is if there are alternate/better/more-efficient ways to do the same thing.

1

1 Answers

1
votes

You can use this XSLT:

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

  <xsl:output method="xml" indent="yes"/>

  <xsl:key name="k" match="titles/*[text()]" use="substring(name(), 6)"/>

  <xsl:template match="/table">    
    <table>
      <tr>
        <xsl:apply-templates select="titles/*[text()]"/>
      </tr>
      <xsl:apply-templates select="valueRows/valueRow"/>
    </table>
  </xsl:template>

  <xsl:template match="titles/*">
    <th>
      <xsl:value-of select="."/>
    </th>
  </xsl:template>

  <xsl:template match="valueRow">
    <tr>
      <xsl:apply-templates select="*[key('k', substring(name(), 6))]"/>
    </tr>
  </xsl:template>

  <xsl:template match="valueRow/*">
    <td>
      <xsl:value-of select="."/>
    </td>
  </xsl:template>

</xsl:stylesheet>

Applied to your XML it will produce desired output:

<table>
  <tr>
    <th>Header Column One</th>
    <th>Header Column Three</th>
  </tr>
  <tr>
    <td>Data Row1 Value1</td>
    <td>Data Row1 Value3</td>
  </tr>
  <tr>
    <td>Data Row2 Value1</td>
    <td>Data Row2 Value3</td>
  </tr>
</table>