Alastair
Alastair

Reputation: 1773

Calculating Differences Using XSLT

I'm trying to extract some information from an xml file using xslt. I've used the xslt math functions to output the differences between start and end times of some actions that I'm iterating over. However, I'm not certain how I can extend this to work out the difference between the start time of the next action and end time of the previous one. As far as I was aware you can't reassign a variable in xslt. In a conventional language I'd just store the last time encountered as variable. If anyone that could give me some pointers on how to do this in a more idiomatic xslt way I'd be most grateful.

Here's a simplified version of my data file.

<Actions>
    <Action>
            <Start>1</Start>
            <End>10</End>
    </Action>
    <Action>
            <Start>13</Start>
            <End>16</End>
    </Action>
    <Action>
            <Start>20</Start>
            <End>24</End>
    </Action>
</Actions>

This is my current xslt transformation that just does difference internally on actions.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="text" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
    <xsl:for-each select="Actions/Action">
        <xsl:value-of select="End - Start" /><xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

The result I'd like to see is

9
3
3
4
4

Upvotes: 3

Views: 318

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243509

This short and simple transformation (no xsl:if, no axes):

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

 <xsl:variable name="vValues" select="/*/*/*"/>

 <xsl:template match="/">
     <xsl:for-each select="$vValues[position() >1]">
       <xsl:variable name="vPos" select="position()"/>

       <xsl:value-of select=". - $vValues[$vPos]"/>
       <xsl:text>&#xA;</xsl:text>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<Actions>
    <Action>
        <Start>1</Start>
        <End>10</End>
    </Action>
    <Action>
        <Start>13</Start>
        <End>16</End>
    </Action>
    <Action>
        <Start>20</Start>
        <End>24</End>
    </Action>
</Actions>

produces the wanted, correct result:

9
3
3
4
4

Upvotes: 1

Kirill Polishchuk
Kirill Polishchuk

Reputation: 56182

Use:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/Actions/Action">
    <xsl:value-of select="End - Start"/>
    <xsl:if test="following-sibling::Action">
      <xsl:text>&#xA;</xsl:text>
      <xsl:value-of select="following-sibling::Action/Start - End"/>
      <xsl:text>&#xA;</xsl:text>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Output:

9
3
3
4
4

Upvotes: 1

Related Questions