<xsl:if>

Sometimes we need conditional processing rules. We might want create a list of sender and recipients with a defined value for the attribute id. In the given example this is only valid for the (unique) sender and the recipient <to id="eve">Eve Intruder</to>. We assume this set of persons shall be inserted into a relational database table Customer consisting of two NOT NULL columns id an name. Thus both attributes must be specified and we must exclude <from> or <to> nodes with undefined id attributes:

Figure 961. Exporting SQL statements. Create comment in forum
...
<xsl:variable name="newline" ❶> <!-- A newline \n -->
  <xsl:text>
</xsl:text>
</xsl:variable>

<xsl:template match="/memo">
  <xsl:for-each select="from|to" ❷>
    <xsl:if test="@id" ❸>
      <xsl:text>INSERT INTO Customer (id, name) VALUES ('</xsl:text>
      <xsl:value-of select="@id" ❹/>
      <xsl:text>', '</xsl:text>
      <xsl:value-of select="." ❺/>
      <xsl:text>')</xsl:text>
      <xsl:value-of select="$newline" ❻/>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

We want to export data from XML documents to a database server. For this purpose INSERT statements are being crafted from a XML document containing relevant data.


Define a file local variable newline. Dealing with text output frequently requires the insertion of newlines. Due to the syntax of the xsl:text elements this tends to clutter the code.

Iterate over the set of the sender node and all recipient nodes.

The attribute value of test will be evaluated as a boolean. In this example it evaluates to true if the attribute id is defined for the context node. Since we are inside the xsl:for-each block all context nodes are either of type <from> or <to> and thus may have an id attribute.

The id attributes value is copied to the output. The @ character in select="@id" tells the XSL processor to read the value of an attribute with name id rather then the content of a nested subelement like in <to id="foo"><id>I am nested!</id></to>.

As stated earlier the dot . denotes the current context element. In this example simply the #PCDATA content is copied to the output.

The $ sign in front of newline tells the XSL processor to access the variable newline previously defined in ❶ rather then interpreting it as the name of a sub element or an attribute.

As expected the recipient entry Adam Hacker does not appear due to the fact that no id attribute is defined in its <to> element:

INSERT INTO Customer (id, name) VALUES ('goik', 'Martin Goik')
INSERT INTO Customer (id, name) VALUES ('eve', 'Eve intruder')

exercise No. 53

The XPath functions position() and last() Create comment in forum

Q:

Revisiting our recipient list in Figure 958, “List of recipient nodes iteration. ” we are interested avoiding the trailing comma:

Adam Hacker,Eve Intruder,

We may use a xsl:if to insert a comma for all but the very last recipient node. This can be achieved by using the XSL functions position() and last(). Hint: The arithmetic operator < may be used in XSL to compare two integer numbers. However it must be escaped as &lt; in order to XML markup clashes.

A:

We have to exclude the comma for the last node of the recipient list. If we have e.g. 10 recipients the function position() will return values integer values starting at 1 and ending with 10. So for the last node the comparison 10 < 10 will evaluate to false:

<xsl:for-each select="memo/to">
  <xsl:value-of select="."/>
  <xsl:if test="position() &lt; last()">
    <xsl:text>,</xsl:text>
  </xsl:if>
</xsl:for-each>

Avoiding xsl:if

In Figure 961, “Exporting SQL statements. ” we used the XPath value from|to to select the desired sender and recipient nodes. Inside the xsl:for-each block we permitted only those nodes which have an id attribute. These two steps may be combined into a single XPath expression obsoleting the xsl:if.

A:

We simply need a modified XPath in the for-each:

<xsl:for-each select="from[@id]|to[@id]">
  <xsl:text>INSERT INTO Customer (id, name) VALUES ('</xsl:text>
  <xsl:value-of select="@id"/>
  <xsl:text>', '</xsl:text>
  <xsl:value-of select="."/>
  <xsl:text>')</xsl:text>
  <xsl:value-of select="$newline"/>
</xsl:for-each>