XSLT - Iterate over a collection even if there are no elements

I have the following XSL file:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="/EMPLOYEE">
    <ROOT>
      <xsl:for-each select="ADDRESSES">
        <xsl:variable name="ADDRESSES" select="." />
        <xsl:for-each select="RENTED_FLATS">
            <xsl:variable name="RENTED_FLATS" select="." />
            <xsl:element name="RENT_DATA">
              <xsl:element name="ADDRESS">
                <xsl:value-of select="$ADDRESSES/LINE1" />
              </xsl:element>
              <xsl:element name="FLOOR">
                <xsl:value-of select="$RENTED_FLATS/FLOOR" />
              </xsl:element>
            </xsl:element>
      </xsl:for-each>
    </ROOT>
  </xsl:template>
</xsl:stylesheet>

The logic is simple: given an input file of ADDRESSES, I want to get the Renting data of each address.

The problem with my implementation is that if there are no nodes "RENTED_FLATS" then it won't enter inside the loop, but I require that, if there's an address without RENTED_FLATS, it still must appear in the output with the element FLOOR empty.

How can I achieve that in XSL?

Thanks and kind regards

Upvotes: 0

Views: 395

Answers (1)

Ian Roberts
Ian Roberts

Reputation: 122414

I would approach this by replacing the uses of for-each with apply-templates, and defining different templates for those ADDRESS elements that do have RENTED_FLATS and those those that don't.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/EMPLOYEE">
    <ROOT>
      <xsl:apply-templates select="ADDRESSES"/>
    </ROOT>
  </xsl:template>

  <xsl:template match="ADDRESSES[RENTED_FLATS]">
    <xsl:apply-templates select="RENTED_FLATS"/>
  </xsl:template>

  <!-- template for addresses that don't match the more specific one above -->
  <xsl:template match="ADDRESSES">
    <RENT_DATA>
      <ADDRESS><xsl:value-of select="LINE1" /></ADDRESS>
      <FLOOR/>
    </RENT_DATA>
  </xsl:template>

  <xsl:template match="RENTED_FLATS">
    <RENT_DATA>
      <ADDRESS><xsl:value-of select="../LINE1" /></ADDRESS>
      <FLOOR><xsl:value-of select="FLOOR" /></FLOOR>
    </RENT_DATA>
  </xsl:template>
</xsl:stylesheet>

Also notice that I've used literal result elements instead of xsl:element in the cases where the element name is fixed - you only need xsl:element when the name has to be calculated dynamically.

Upvotes: 1

Related Questions